• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tcalmant / python-javaobj / 8591112367

07 Apr 2024 07:18PM UTC coverage: 78.701%. First build
8591112367

push

github

tcalmant
Added 3.11 & 3.12 to GitHub actions

1611 of 2047 relevant lines covered (78.7%)

4.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.47
/javaobj/v1/transformers.py
1
#!/usr/bin/python
2
# -- Content-Encoding: utf-8 --
3
"""
6✔
4
Implementation of the object transformers in v1 parser
5

6
:authors: Volodymyr Buell, Thomas Calmant
7
:license: Apache License 2.0
8
:version: 0.4.4
9
:status: Alpha
10

11
..
12

13
    Copyright 2024 Thomas Calmant
14

15
    Licensed under the Apache License, Version 2.0 (the "License");
16
    you may not use this file except in compliance with the License.
17
    You may obtain a copy of the License at
18

19
        http://www.apache.org/licenses/LICENSE-2.0
20

21
    Unless required by applicable law or agreed to in writing, software
22
    distributed under the License is distributed on an "AS IS" BASIS,
23
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
    See the License for the specific language governing permissions and
25
    limitations under the License.
26
"""
27

28
from __future__ import absolute_import
6✔
29

30
from typing import Callable, Dict
6✔
31
import functools
6✔
32

33
from .beans import JavaClass, JavaObject
6✔
34
from .unmarshaller import JavaObjectUnmarshaller
6✔
35
from ..constants import ClassDescFlags, TerminalCode, TypeCode
6✔
36
from ..utils import (
6✔
37
    log_debug,
38
    log_error,
39
    to_bytes,
40
    read_struct,
41
    read_string,
42
)
43

44

45
__all__ = ("DefaultObjectTransformer",)
6✔
46

47

48
class DefaultObjectTransformer(object):  # pylint:disable=R0205
6✔
49
    """
50
    Default transformer for the deserialized objects.
51
    Converts JavaObject objects to Python types (maps, lists, ...)
52
    """
53

54
    class JavaList(list, JavaObject):
6✔
55
        """
56
        Python-Java list bridge type
57
        """
58

59
        def __init__(self, unmarshaller):
6✔
60
            # type: (JavaObjectUnmarshaller) -> None
61
            list.__init__(self)
6✔
62
            JavaObject.__init__(self)
6✔
63

64
        def __hash__(self):
6✔
65
            return list.__hash__(self)
×
66

67
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
68
            # type: (JavaObjectUnmarshaller, int) -> None
69
            """
70
            Loads the content of the map, written with a custom implementation
71
            """
72
            # Lists have their content in there annotations
73
            self.extend(self.annotations[1:])
6✔
74

75
    @functools.total_ordering
6✔
76
    class JavaPrimitiveClass(JavaObject):
6✔
77
        """
78
        Parent of Java classes matching a primitive (Bool, Integer, Long, ...)
79
        """
80

81
        def __init__(self, unmarshaller):
6✔
82
            JavaObject.__init__(self)
6✔
83
            self.value = None
6✔
84

85
        def __str__(self):
6✔
86
            return str(self.value)
6✔
87

88
        def __repr__(self):
6✔
89
            return repr(self.value)
6✔
90

91
        def __hash__(self):
6✔
92
            return hash(self.value)
6✔
93

94
        def __eq__(self, other):
6✔
95
            return self.value == other
6✔
96

97
        def __lt__(self, other):
6✔
98
            return self.value < other
×
99

100
    class JavaBool(JavaPrimitiveClass):
6✔
101
        def __bool__(self):
6✔
102
            return self.value
×
103

104
    class JavaInt(JavaPrimitiveClass):
6✔
105
        def __int__(self):
6✔
106
            return self.value
×
107

108
    class JavaMap(dict, JavaObject):
6✔
109
        """
110
        Python-Java dictionary/map bridge type
111
        """
112

113
        def __init__(self, unmarshaller):
6✔
114
            # type: (JavaObjectUnmarshaller) -> None
115
            dict.__init__(self)
6✔
116
            JavaObject.__init__(self)
6✔
117

118
        def __hash__(self):
6✔
119
            return dict.__hash__(self)
×
120

121
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
122
            # type: (JavaObjectUnmarshaller, int) -> None
123
            """
124
            Loads the content of the map, written with a custom implementation
125
            """
126
            # Group annotation elements 2 by 2
127
            args = [iter(self.annotations[1:])] * 2
6✔
128
            for key, value in zip(*args):
6✔
129
                self[key] = value
6✔
130

131
    class JavaLinkedHashMap(JavaMap):
6✔
132
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
133
            # type: (JavaObjectUnmarshaller, int) -> None
134
            """
135
            Loads the content of the map, written with a custom implementation
136
            """
137
            # Ignore the blockdata opid
138
            (opid,) = unmarshaller._readStruct(">B")
×
139
            if opid != ClassDescFlags.SC_BLOCK_DATA:
×
140
                raise ValueError("Start of block data not found")
×
141

142
            # Read HashMap fields
143
            self.buckets = unmarshaller._read_value(
×
144
                TypeCode.TYPE_INTEGER, ident
145
            )
146
            self.size = unmarshaller._read_value(TypeCode.TYPE_INTEGER, ident)
×
147

148
            # Read entries
149
            for _ in range(self.size):
×
150
                key = unmarshaller._read_and_exec_opcode()[1]
×
151
                value = unmarshaller._read_and_exec_opcode()[1]
×
152
                self[key] = value
×
153

154
            # Ignore the end of the blockdata
155
            unmarshaller._read_and_exec_opcode(
×
156
                ident, [TerminalCode.TC_ENDBLOCKDATA]
157
            )
158

159
            # Ignore the trailing 0
160
            (opid,) = unmarshaller._readStruct(">B")
×
161
            if opid != 0:
×
162
                raise ValueError("Should find 0x0, got {0:x}".format(opid))
×
163

164
    class JavaSet(set, JavaObject):
6✔
165
        """
166
        Python-Java set bridge type
167
        """
168

169
        def __init__(self, unmarshaller):
6✔
170
            # type: (JavaObjectUnmarshaller) -> None
171
            set.__init__(self)
6✔
172
            JavaObject.__init__(self)
6✔
173

174
        def __hash__(self):
6✔
175
            return set.__hash__(self)
×
176

177
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
178
            # type: (JavaObjectUnmarshaller, int) -> None
179
            """
180
            Loads the content of the map, written with a custom implementation
181
            """
182
            self.update(self.annotations[1:])
6✔
183

184
    class JavaTreeSet(JavaSet):
6✔
185
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
186
            # type: (JavaObjectUnmarshaller, int) -> None
187
            """
188
            Loads the content of the map, written with a custom implementation
189
            """
190
            # Annotation[1] == size of the set
191
            self.update(self.annotations[2:])
6✔
192

193
    class JavaTime(JavaObject):
6✔
194
        """
195
        Represents the classes found in the java.time package
196

197
        The semantic of the fields depends on the type of time that has been
198
        parsed
199
        """
200

201
        DURATION_TYPE = 1
6✔
202
        INSTANT_TYPE = 2
6✔
203
        LOCAL_DATE_TYPE = 3
6✔
204
        LOCAL_TIME_TYPE = 4
6✔
205
        LOCAL_DATE_TIME_TYPE = 5
6✔
206
        ZONE_DATE_TIME_TYPE = 6
6✔
207
        ZONE_REGION_TYPE = 7
6✔
208
        ZONE_OFFSET_TYPE = 8
6✔
209
        OFFSET_TIME_TYPE = 9
6✔
210
        OFFSET_DATE_TIME_TYPE = 10
6✔
211
        YEAR_TYPE = 11
6✔
212
        YEAR_MONTH_TYPE = 12
6✔
213
        MONTH_DAY_TYPE = 13
6✔
214
        PERIOD_TYPE = 14
6✔
215

216
        def __init__(self, unmarshaller):
6✔
217
            # type: (JavaObjectUnmarshaller) -> None
218
            JavaObject.__init__(self)
6✔
219
            self.type = -1
6✔
220
            self.year = None
6✔
221
            self.month = None
6✔
222
            self.day = None
6✔
223
            self.hour = None
6✔
224
            self.minute = None
6✔
225
            self.second = None
6✔
226
            self.nano = None
6✔
227
            self.offset = None
6✔
228
            self.zone = None
6✔
229

230
            self.time_handlers = {
6✔
231
                self.DURATION_TYPE: self.do_duration,
232
                self.INSTANT_TYPE: self.do_instant,
233
                self.LOCAL_DATE_TYPE: self.do_local_date,
234
                self.LOCAL_DATE_TIME_TYPE: self.do_local_date_time,
235
                self.LOCAL_TIME_TYPE: self.do_local_time,
236
                self.ZONE_DATE_TIME_TYPE: self.do_zoned_date_time,
237
                self.ZONE_OFFSET_TYPE: self.do_zone_offset,
238
                self.ZONE_REGION_TYPE: self.do_zone_region,
239
                self.OFFSET_TIME_TYPE: self.do_offset_time,
240
                self.OFFSET_DATE_TIME_TYPE: self.do_offset_date_time,
241
                self.YEAR_TYPE: self.do_year,
242
                self.YEAR_MONTH_TYPE: self.do_year_month,
243
                self.MONTH_DAY_TYPE: self.do_month_day,
244
                self.PERIOD_TYPE: self.do_period,
245
            }
246

247
        def __str__(self):
6✔
248
            return (
6✔
249
                "JavaTime(type=0x{s.type}, "
250
                "year={s.year}, month={s.month}, day={s.day}, "
251
                "hour={s.hour}, minute={s.minute}, second={s.second}, "
252
                "nano={s.nano}, offset={s.offset}, zone={s.zone})"
253
            ).format(s=self)
254

255
        def __extra_loading__(self, unmarshaller, ident=0):
6✔
256
            # type: (JavaObjectUnmarshaller, int) -> None
257
            """
258
            Loads the content of the map, written with a custom implementation
259
            """
260
            # Convert back annotations to bytes
261
            # latin-1 is used to ensure that bytes are kept as is
262
            content = to_bytes(self.annotations[0], "latin1")
6✔
263
            (self.type,), content = read_struct(content, ">b")
6✔
264

265
            try:
6✔
266
                self.time_handlers[self.type](unmarshaller, content)
6✔
267
            except KeyError as ex:
×
268
                log_error("Unhandled kind of time: {}".format(ex))
×
269

270
        def do_duration(self, unmarshaller, data):
6✔
271
            (self.second, self.nano), data = read_struct(data, ">qi")
6✔
272
            return data
6✔
273

274
        def do_instant(self, unmarshaller, data):
6✔
275
            (self.second, self.nano), data = read_struct(data, ">qi")
6✔
276
            return data
6✔
277

278
        def do_local_date(self, unmarshaller, data):
6✔
279
            (self.year, self.month, self.day), data = read_struct(data, ">ibb")
6✔
280
            return data
6✔
281

282
        def do_local_time(self, unmarshaller, data):
6✔
283
            (hour,), data = read_struct(data, ">b")
6✔
284
            minute = 0
6✔
285
            second = 0
6✔
286
            nano = 0
6✔
287

288
            if hour < 0:
6✔
289
                hour = ~hour
×
290
            else:
291
                (minute,), data = read_struct(data, ">b")
6✔
292
                if minute < 0:
6✔
293
                    minute = ~minute
×
294
                else:
295
                    (second,), data = read_struct(data, ">b")
6✔
296
                    if second < 0:
6✔
297
                        second = ~second
×
298
                    else:
299
                        (nano,), data = read_struct(data, ">i")
6✔
300

301
            self.hour = hour
6✔
302
            self.minute = minute
6✔
303
            self.second = second
6✔
304
            self.nano = nano
6✔
305
            return data
6✔
306

307
        def do_local_date_time(self, unmarshaller, data):
6✔
308
            data = self.do_local_date(unmarshaller, data)
6✔
309
            data = self.do_local_time(unmarshaller, data)
6✔
310
            return data
6✔
311

312
        def do_zoned_date_time(self, unmarshaller, data):
6✔
313
            data = self.do_local_date_time(unmarshaller, data)
6✔
314
            data = self.do_zone_offset(unmarshaller, data)
6✔
315
            data = self.do_zone_region(unmarshaller, data)
6✔
316
            return data
6✔
317

318
        def do_zone_offset(self, unmarshaller, data):
6✔
319
            (offset_byte,), data = read_struct(data, ">b")
6✔
320
            if offset_byte == 127:
6✔
321
                (self.offset,), data = read_struct(data, ">i")
×
322
            else:
323
                self.offset = offset_byte * 900
6✔
324
            return data
6✔
325

326
        def do_zone_region(self, unmarshaller, data):
6✔
327
            self.zone, data = read_string(data)
6✔
328
            return data
6✔
329

330
        def do_offset_time(self, unmarshaller, data):
6✔
331
            data = self.do_local_time(unmarshaller, data)
×
332
            data = self.do_zone_offset(unmarshaller, data)
×
333
            return data
×
334

335
        def do_offset_date_time(self, unmarshaller, data):
6✔
336
            data = self.do_local_date_time(unmarshaller, data)
×
337
            data = self.do_zone_offset(unmarshaller, data)
×
338
            return data
×
339

340
        def do_year(self, unmarshaller, data):
6✔
341
            (self.year,), data = read_struct(data, ">i")
×
342
            return data
×
343

344
        def do_year_month(self, unmarshaller, data):
6✔
345
            (self.year, self.month), data = read_struct(data, ">ib")
×
346
            return data
×
347

348
        def do_month_day(self, unmarshaller, data):
6✔
349
            (self.month, self.day), data = read_struct(data, ">bb")
×
350
            return data
×
351

352
        def do_period(self, unmarshaller, data):
6✔
353
            (self.year, self.month, self.day), data = read_struct(data, ">iii")
×
354
            return data
×
355

356
    TYPE_MAPPER = {
6✔
357
        "java.util.ArrayList": JavaList,
358
        "java.util.LinkedList": JavaList,
359
        "java.util.HashMap": JavaMap,
360
        "java.util.LinkedHashMap": JavaLinkedHashMap,
361
        "java.util.TreeMap": JavaMap,
362
        "java.util.HashSet": JavaSet,
363
        "java.util.LinkedHashSet": JavaSet,
364
        "java.util.TreeSet": JavaTreeSet,
365
        "java.time.Ser": JavaTime,
366
        "java.lang.Boolean": JavaBool,
367
        "java.lang.Integer": JavaInt,
368
        "java.lang.Long": JavaInt,
369
    }  # type: Dict[str, Callable[[JavaObjectUnmarshaller], JavaObject]]
370

371
    def create(self, classdesc, unmarshaller):
6✔
372
        # type: (JavaClass, JavaObjectUnmarshaller) -> JavaObject
373
        """
374
        Transforms a deserialized Java object into a Python object
375

376
        :param classdesc: The description of a Java class
377
        :return: The Python form of the object, or the original JavaObject
378
        """
379
        try:
6✔
380
            mapped_type = self.TYPE_MAPPER[classdesc.name]
6✔
381
        except KeyError:
6✔
382
            # Return a JavaObject by default
383
            return JavaObject()
6✔
384
        else:
385
            log_debug("---")
6✔
386
            log_debug(classdesc.name)
6✔
387
            log_debug("---")
6✔
388

389
            java_object = mapped_type(unmarshaller)
6✔
390

391
            log_debug(">>> java_object: {0}".format(java_object))
6✔
392
            return java_object
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc