structurize 2.17.0__py3-none-any.whl → 2.18.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- avrotize/_version.py +3 -3
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/METADATA +1 -1
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/RECORD +7 -8
- avrotize/structuretojava.py +0 -853
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/WHEEL +0 -0
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/entry_points.txt +0 -0
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/licenses/LICENSE +0 -0
- {structurize-2.17.0.dist-info → structurize-2.18.1.dist-info}/top_level.txt +0 -0
avrotize/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.18.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 18, 1)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gdae473138'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structurize
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.18.1
|
|
4
4
|
Summary: Tools to convert from and to JSON Structure from various other schema languages.
|
|
5
5
|
Author-email: Clemens Vasters <clemensv@microsoft.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
avrotize/__init__.py,sha256=JjPSX7c686TV00J_x0Py9JwXS0aJl8vpLn81Y0ondkw,3606
|
|
2
2
|
avrotize/__main__.py,sha256=5pY8dYAURcOnFRvgb6fgaOIa_SOzPLIWbU8-ZTQ0jG4,88
|
|
3
|
-
avrotize/_version.py,sha256=
|
|
3
|
+
avrotize/_version.py,sha256=tMC0K6V8_fTSI319xo2OQmmHkpAHWOafG-61857ehCk,714
|
|
4
4
|
avrotize/asn1toavro.py,sha256=QDNwfBfXMxSH-k487CA3CaGCGDzOLs4PpVbbENm5uF0,8386
|
|
5
5
|
avrotize/avrotize.py,sha256=VHFpBltMVBpyt0ju3ZWW725BKjQ4Fk-nrAy8udW-X44,5713
|
|
6
6
|
avrotize/avrotocpp.py,sha256=hRZV247_TDD7Sm6_8sFx-UH5SueLLx2Wg6TvAVUX0iE,25693
|
|
@@ -47,7 +47,6 @@ avrotize/structuretodb.py,sha256=3QE_TCdNklGH5ymzGsEnX1sI4OhvX2AYKPH7xtR5tHk,439
|
|
|
47
47
|
avrotize/structuretogo.py,sha256=VCEUz-5J8uRqX1hWaTimtfVzEsIB-gs4wxa308rYD0s,32470
|
|
48
48
|
avrotize/structuretographql.py,sha256=wcGXnrup5v5saRa1BhR6o-X8q8ujsQMVqrFHQTBPjww,20468
|
|
49
49
|
avrotize/structuretoiceberg.py,sha256=itKb33Kj-7-udk4eHTLmTEasIeh1ggpZ3e_bwCxLABM,15344
|
|
50
|
-
avrotize/structuretojava.py,sha256=jG2Vcf1KdezWrZo5lsecxLnmnMw1rA96uOxVWJQ4Rso,43372
|
|
51
50
|
avrotize/structuretojsons.py,sha256=PJrQBaf6yQHu5eFkePxbjPBEmL-fYfX2wj6OmH1jsWw,22495
|
|
52
51
|
avrotize/structuretokusto.py,sha256=rOKgYIcm7ZK8RS-VvMFPNzPzwtv7c4dIKU-fKjrJLyM,30618
|
|
53
52
|
avrotize/structuretomd.py,sha256=exfCldYbieVdduhotSoLrxsbphmyJQyeQso9qv4qyUw,13642
|
|
@@ -57,9 +56,9 @@ avrotize/structuretorust.py,sha256=ChRmO7uzU-pMdDdS0Vtg-MVUaOaNhNUPwH-ZKKOHglU,3
|
|
|
57
56
|
avrotize/structuretots.py,sha256=PLV6W8k-yd7xkspUaQ-Vj90F26PTkB5HO0OkPJolkJ0,30800
|
|
58
57
|
avrotize/structuretoxsd.py,sha256=01VpasyWSMOx04sILHLP7H-WkhGdXAEGKohUUfgrNf0,32797
|
|
59
58
|
avrotize/xsdtoavro.py,sha256=nQtNH_3pEZBp67oUCPqzhvItEExHTe-8obsIfNRXt8Y,19064
|
|
60
|
-
structurize-2.
|
|
61
|
-
structurize-2.
|
|
62
|
-
structurize-2.
|
|
63
|
-
structurize-2.
|
|
64
|
-
structurize-2.
|
|
65
|
-
structurize-2.
|
|
59
|
+
structurize-2.18.1.dist-info/licenses/LICENSE,sha256=xGtQGygTETTtDQJafZCUbpsed3GxO6grmqig-jGEuSk,11348
|
|
60
|
+
structurize-2.18.1.dist-info/METADATA,sha256=xN6pWXUXX1yXFT4kOEPm42CktTuLNrVPl8LUiWU_Sg8,3670
|
|
61
|
+
structurize-2.18.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
62
|
+
structurize-2.18.1.dist-info/entry_points.txt,sha256=biIH7jA5auhVqfbwHVk2gmD_gvrPYKgjpCAn0JWZ-Rs,55
|
|
63
|
+
structurize-2.18.1.dist-info/top_level.txt,sha256=yn-yQ0Cm1O9fbF8KJgv4IIvX4YRGelKgPqZF1wS5P50,9
|
|
64
|
+
structurize-2.18.1.dist-info/RECORD,,
|
avrotize/structuretojava.py
DELETED
|
@@ -1,853 +0,0 @@
|
|
|
1
|
-
# pylint: disable=too-many-arguments, too-many-locals, too-many-branches, too-many-statements, line-too-long
|
|
2
|
-
|
|
3
|
-
""" Generates Java classes from JSON Structure schema """
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
from typing import Dict, List, Tuple, Union, Set, Optional, Any
|
|
7
|
-
from avrotize.constants import JACKSON_VERSION
|
|
8
|
-
|
|
9
|
-
from avrotize.common import pascal, camel, process_template
|
|
10
|
-
|
|
11
|
-
INDENT = ' '
|
|
12
|
-
|
|
13
|
-
JsonNode = Dict[str, 'JsonNode'] | List['JsonNode'] | str | None
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def flatten_type_name(name: str) -> str:
|
|
17
|
-
"""Strips the namespace from a name"""
|
|
18
|
-
if name.endswith('[]'):
|
|
19
|
-
return flatten_type_name(name[:-2]+'Array')
|
|
20
|
-
base_name = pascal(name.replace(' ', '').split('.')[-1].replace('>', '').replace('<', '').replace(',', ''))
|
|
21
|
-
return base_name
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def is_java_reserved_word(word: str) -> bool:
|
|
25
|
-
"""Checks if a word is a Java reserved word"""
|
|
26
|
-
reserved_words = [
|
|
27
|
-
'abstract', 'assert', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const',
|
|
28
|
-
'continue', 'default', 'do', 'double', 'else', 'enum', 'extends', 'final', 'finally', 'float',
|
|
29
|
-
'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'int', 'interface', 'long', 'native',
|
|
30
|
-
'new', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp',
|
|
31
|
-
'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'try', 'void', 'volatile',
|
|
32
|
-
'while', 'true', 'false', 'null', 'record',
|
|
33
|
-
]
|
|
34
|
-
return word in reserved_words
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class StructureToJava:
|
|
38
|
-
"""Converts JSON Structure schema to Java classes, including Jackson annotations"""
|
|
39
|
-
|
|
40
|
-
def __init__(self, base_package: str = '') -> None:
|
|
41
|
-
self.base_package = base_package.replace('.', '/')
|
|
42
|
-
self.output_dir = os.getcwd()
|
|
43
|
-
self.jackson_annotations = True # Always use Jackson annotations for JSON Structure
|
|
44
|
-
self.pascal_properties = False
|
|
45
|
-
self.generated_types_structure_namespace: Dict[str,str] = {}
|
|
46
|
-
self.generated_types_java_package: Dict[str,str] = {}
|
|
47
|
-
self.schema_doc: JsonNode = None
|
|
48
|
-
self.schema_registry: Dict[str, Dict] = {}
|
|
49
|
-
|
|
50
|
-
def qualified_name(self, package: str, name: str) -> str:
|
|
51
|
-
"""Concatenates package and name using a dot separator"""
|
|
52
|
-
slash_package_name = package.replace('.', '/')
|
|
53
|
-
safe_package_slash = self.safe_package(slash_package_name.lower())
|
|
54
|
-
safe_package = safe_package_slash.replace('/', '.')
|
|
55
|
-
return f"{safe_package}.{name}" if package else name
|
|
56
|
-
|
|
57
|
-
def join_packages(self, parent_package: str, package: str) -> str:
|
|
58
|
-
"""Joins package and name using a dot separator"""
|
|
59
|
-
if parent_package and package:
|
|
60
|
-
return f"{parent_package}.{package}".lower()
|
|
61
|
-
elif parent_package:
|
|
62
|
-
return parent_package.lower()
|
|
63
|
-
return package.lower()
|
|
64
|
-
|
|
65
|
-
class JavaType:
|
|
66
|
-
"""Java type definition"""
|
|
67
|
-
|
|
68
|
-
def __init__(self, type_name: str, union_types: List['StructureToJava.JavaType'] | None = None, is_class: bool = False, is_enum: bool = False) -> None:
|
|
69
|
-
self.type_name = type_name
|
|
70
|
-
self.union_types = union_types
|
|
71
|
-
self.is_class = is_class
|
|
72
|
-
self.is_enum = is_enum
|
|
73
|
-
|
|
74
|
-
def safe_identifier(self, name: str, class_name: str = '') -> str:
|
|
75
|
-
"""Converts a name to a safe Java identifier"""
|
|
76
|
-
if is_java_reserved_word(name):
|
|
77
|
-
return f"_{name}"
|
|
78
|
-
if class_name and name == class_name:
|
|
79
|
-
return f"{name}_"
|
|
80
|
-
return name
|
|
81
|
-
|
|
82
|
-
def safe_package(self, packageName: str) -> str:
|
|
83
|
-
"""Converts a name to a safe Java identifier by checking each path segment"""
|
|
84
|
-
segments = packageName.split('/')
|
|
85
|
-
safe_segments = [
|
|
86
|
-
self.safe_identifier(segment)
|
|
87
|
-
for segment in segments
|
|
88
|
-
]
|
|
89
|
-
|
|
90
|
-
return '/'.join(safe_segments)
|
|
91
|
-
|
|
92
|
-
def map_primitive_to_java(self, structure_type: str, is_optional: bool) -> JavaType:
|
|
93
|
-
"""Maps JSON Structure primitive types to Java types"""
|
|
94
|
-
optional_mapping = {
|
|
95
|
-
'null': 'Void',
|
|
96
|
-
'boolean': 'Boolean',
|
|
97
|
-
'string': 'String',
|
|
98
|
-
'integer': 'Integer',
|
|
99
|
-
'number': 'Double',
|
|
100
|
-
'int8': 'Byte',
|
|
101
|
-
'uint8': 'Short', # Java doesn't have unsigned byte, use Short
|
|
102
|
-
'int16': 'Short',
|
|
103
|
-
'uint16': 'Integer', # Java doesn't have unsigned short, use Integer
|
|
104
|
-
'int32': 'Integer',
|
|
105
|
-
'uint32': 'Long', # Java doesn't have unsigned int, use Long
|
|
106
|
-
'int64': 'Long',
|
|
107
|
-
'uint64': 'BigInteger', # Use BigInteger for uint64 to handle full range in JSON
|
|
108
|
-
'int128': 'BigInteger',
|
|
109
|
-
'uint128': 'BigInteger',
|
|
110
|
-
'float8': 'Float',
|
|
111
|
-
'float': 'Float',
|
|
112
|
-
'double': 'Double',
|
|
113
|
-
'binary32': 'Float',
|
|
114
|
-
'binary64': 'Double',
|
|
115
|
-
'decimal': 'BigDecimal',
|
|
116
|
-
'binary': 'byte[]',
|
|
117
|
-
'date': 'LocalDate',
|
|
118
|
-
'time': 'LocalTime',
|
|
119
|
-
'datetime': 'Instant',
|
|
120
|
-
'timestamp': 'Instant',
|
|
121
|
-
'duration': 'Duration',
|
|
122
|
-
'uuid': 'UUID',
|
|
123
|
-
'uri': 'URI',
|
|
124
|
-
'jsonpointer': 'String',
|
|
125
|
-
'any': 'Object'
|
|
126
|
-
}
|
|
127
|
-
required_mapping = {
|
|
128
|
-
'null': 'void',
|
|
129
|
-
'boolean': 'boolean',
|
|
130
|
-
'string': 'String',
|
|
131
|
-
'integer': 'int',
|
|
132
|
-
'number': 'double',
|
|
133
|
-
'int8': 'byte',
|
|
134
|
-
'uint8': 'short',
|
|
135
|
-
'int16': 'short',
|
|
136
|
-
'uint16': 'int',
|
|
137
|
-
'int32': 'int',
|
|
138
|
-
'uint32': 'long',
|
|
139
|
-
'int64': 'long',
|
|
140
|
-
'uint64': 'BigInteger', # Use BigInteger for uint64 to handle full range in JSON
|
|
141
|
-
'int128': 'BigInteger',
|
|
142
|
-
'uint128': 'BigInteger',
|
|
143
|
-
'float8': 'float',
|
|
144
|
-
'float': 'float',
|
|
145
|
-
'double': 'double',
|
|
146
|
-
'binary32': 'float',
|
|
147
|
-
'binary64': 'double',
|
|
148
|
-
'decimal': 'BigDecimal',
|
|
149
|
-
'binary': 'byte[]',
|
|
150
|
-
'date': 'LocalDate',
|
|
151
|
-
'time': 'LocalTime',
|
|
152
|
-
'datetime': 'Instant',
|
|
153
|
-
'timestamp': 'Instant',
|
|
154
|
-
'duration': 'Duration',
|
|
155
|
-
'uuid': 'UUID',
|
|
156
|
-
'uri': 'URI',
|
|
157
|
-
'jsonpointer': 'String',
|
|
158
|
-
'any': 'Object'
|
|
159
|
-
}
|
|
160
|
-
if '.' in structure_type:
|
|
161
|
-
type_name = structure_type.split('.')[-1]
|
|
162
|
-
package_name = '.'.join(structure_type.split('.')[:-1]).lower()
|
|
163
|
-
structure_type = self.qualified_name(package_name, type_name)
|
|
164
|
-
if structure_type in self.generated_types_structure_namespace:
|
|
165
|
-
kind = self.generated_types_structure_namespace[structure_type]
|
|
166
|
-
qualified_class_name = self.qualified_name(self.base_package, structure_type)
|
|
167
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=kind=="class", is_enum=kind=="enum")
|
|
168
|
-
else:
|
|
169
|
-
return StructureToJava.JavaType(required_mapping.get(structure_type, structure_type) if not is_optional else optional_mapping.get(structure_type, structure_type))
|
|
170
|
-
|
|
171
|
-
def is_java_primitive(self, java_type: JavaType) -> bool:
|
|
172
|
-
"""Checks if a Java type is a primitive type"""
|
|
173
|
-
return java_type.type_name in [
|
|
174
|
-
'void', 'boolean', 'int', 'long', 'float', 'double', 'byte', 'short', 'byte[]', 'String',
|
|
175
|
-
'Boolean', 'Integer', 'Long', 'Float', 'Double', 'Short', 'Byte', 'Void']
|
|
176
|
-
|
|
177
|
-
def is_java_optional_type(self, java_type: JavaType) -> bool:
|
|
178
|
-
"""Checks if a Java type is an optional type"""
|
|
179
|
-
return java_type.type_name in ['Void', 'Boolean', 'Integer', 'Long', 'Float', 'Double', 'Short', 'Byte']
|
|
180
|
-
|
|
181
|
-
def is_java_numeric_type(self, java_type: JavaType) -> bool:
|
|
182
|
-
"""Checks if a Java type is a numeric type"""
|
|
183
|
-
return java_type.type_name in ['int', 'long', 'float', 'double', 'short', 'byte', 'Integer', 'Long', 'Float', 'Double', 'Short', 'Byte', 'BigInteger', 'BigDecimal']
|
|
184
|
-
|
|
185
|
-
def is_java_integer_type(self, java_type: JavaType) -> bool:
|
|
186
|
-
"""Checks if a Java type is an integer type"""
|
|
187
|
-
return java_type.type_name in ['int', 'long', 'short', 'byte', 'Integer', 'Long', 'Short', 'Byte', 'BigInteger']
|
|
188
|
-
|
|
189
|
-
def resolve_ref(self, ref: str, context_schema: Optional[Dict] = None) -> Optional[Dict]:
|
|
190
|
-
""" Resolves a $ref to the actual schema definition """
|
|
191
|
-
# Check if it's an absolute URI reference (schema with $id)
|
|
192
|
-
if not ref.startswith('#/'):
|
|
193
|
-
# Try to resolve from schema registry
|
|
194
|
-
if ref in self.schema_registry:
|
|
195
|
-
return self.schema_registry[ref]
|
|
196
|
-
return None
|
|
197
|
-
|
|
198
|
-
# Handle fragment-only references (internal to document)
|
|
199
|
-
path = ref[2:].split('/')
|
|
200
|
-
schema = context_schema if context_schema else self.schema_doc
|
|
201
|
-
|
|
202
|
-
for part in path:
|
|
203
|
-
if not isinstance(schema, dict) or part not in schema:
|
|
204
|
-
return None
|
|
205
|
-
schema = schema[part]
|
|
206
|
-
|
|
207
|
-
return schema
|
|
208
|
-
|
|
209
|
-
def register_schema_ids(self, schema: Dict, base_uri: str = '') -> None:
|
|
210
|
-
""" Recursively registers schemas with $id keywords """
|
|
211
|
-
if not isinstance(schema, dict):
|
|
212
|
-
return
|
|
213
|
-
|
|
214
|
-
# Register this schema if it has an $id
|
|
215
|
-
if '$id' in schema:
|
|
216
|
-
schema_id = schema['$id']
|
|
217
|
-
# Handle relative URIs
|
|
218
|
-
if base_uri and not schema_id.startswith(('http://', 'https://', 'urn:')):
|
|
219
|
-
from urllib.parse import urljoin
|
|
220
|
-
schema_id = urljoin(base_uri, schema_id)
|
|
221
|
-
self.schema_registry[schema_id] = schema
|
|
222
|
-
base_uri = schema_id # Update base URI for nested schemas
|
|
223
|
-
|
|
224
|
-
# Recursively process definitions
|
|
225
|
-
if 'definitions' in schema:
|
|
226
|
-
for def_name, def_schema in schema['definitions'].items():
|
|
227
|
-
if isinstance(def_schema, dict):
|
|
228
|
-
self.register_schema_ids(def_schema, base_uri)
|
|
229
|
-
|
|
230
|
-
# Recursively process properties
|
|
231
|
-
if 'properties' in schema:
|
|
232
|
-
for prop_name, prop_schema in schema['properties'].items():
|
|
233
|
-
if isinstance(prop_schema, dict):
|
|
234
|
-
self.register_schema_ids(prop_schema, base_uri)
|
|
235
|
-
|
|
236
|
-
# Recursively process items, values, etc.
|
|
237
|
-
for key in ['items', 'values', 'additionalProperties']:
|
|
238
|
-
if key in schema and isinstance(schema[key], dict):
|
|
239
|
-
self.register_schema_ids(schema[key], base_uri)
|
|
240
|
-
|
|
241
|
-
def convert_structure_type_to_java(self, class_name: str, field_name: str, structure_type: Union[str, Dict, List], parent_package: str, nullable: bool = False) -> JavaType:
|
|
242
|
-
"""Converts JSON Structure type to Java type"""
|
|
243
|
-
if isinstance(structure_type, str):
|
|
244
|
-
return self.map_primitive_to_java(structure_type, nullable)
|
|
245
|
-
elif isinstance(structure_type, list):
|
|
246
|
-
# Handle type unions
|
|
247
|
-
non_null_types = [t for t in structure_type if t != 'null']
|
|
248
|
-
if len(non_null_types) == 1:
|
|
249
|
-
if isinstance(non_null_types[0], str):
|
|
250
|
-
return self.map_primitive_to_java(non_null_types[0], True)
|
|
251
|
-
else:
|
|
252
|
-
return self.convert_structure_type_to_java(class_name, field_name, non_null_types[0], parent_package)
|
|
253
|
-
else:
|
|
254
|
-
# Multiple non-null types - generate union class
|
|
255
|
-
if self.jackson_annotations:
|
|
256
|
-
return StructureToJava.JavaType(self.generate_embedded_union_class_jackson(class_name, field_name, non_null_types, parent_package, write_file=True), is_class=True)
|
|
257
|
-
else:
|
|
258
|
-
types: List[StructureToJava.JavaType] = [self.convert_structure_type_to_java(
|
|
259
|
-
class_name, field_name, t, parent_package) for t in non_null_types]
|
|
260
|
-
return StructureToJava.JavaType('Object', types)
|
|
261
|
-
elif isinstance(structure_type, dict):
|
|
262
|
-
# Handle $ref
|
|
263
|
-
if '$ref' in structure_type:
|
|
264
|
-
ref_schema = self.resolve_ref(structure_type['$ref'], self.schema_doc if isinstance(self.schema_doc, dict) else None)
|
|
265
|
-
if ref_schema:
|
|
266
|
-
ref_path = structure_type['$ref'].split('/')
|
|
267
|
-
type_name = ref_path[-1]
|
|
268
|
-
ref_namespace = '.'.join(ref_path[2:-1]) if len(ref_path) > 3 else parent_package
|
|
269
|
-
return self.generate_class_or_enum(ref_schema, ref_namespace, write_file=True, explicit_name=type_name)
|
|
270
|
-
return StructureToJava.JavaType('Object')
|
|
271
|
-
|
|
272
|
-
# Handle enum keyword
|
|
273
|
-
if 'enum' in structure_type:
|
|
274
|
-
return self.generate_enum(structure_type, field_name, parent_package, write_file=True)
|
|
275
|
-
|
|
276
|
-
# Handle type keyword
|
|
277
|
-
if 'type' not in structure_type:
|
|
278
|
-
return StructureToJava.JavaType('Object')
|
|
279
|
-
|
|
280
|
-
struct_type = structure_type['type']
|
|
281
|
-
|
|
282
|
-
# Handle complex types
|
|
283
|
-
if struct_type == 'object':
|
|
284
|
-
return self.generate_class(structure_type, parent_package, write_file=True)
|
|
285
|
-
elif struct_type == 'array':
|
|
286
|
-
item_type = self.convert_structure_type_to_java(class_name, field_name, structure_type.get('items', {'type': 'any'}), parent_package, nullable=True).type_name
|
|
287
|
-
return StructureToJava.JavaType(f"List<{item_type}>")
|
|
288
|
-
elif struct_type == 'set':
|
|
289
|
-
item_type = self.convert_structure_type_to_java(class_name, field_name, structure_type.get('items', {'type': 'any'}), parent_package, nullable=True).type_name
|
|
290
|
-
return StructureToJava.JavaType(f"Set<{item_type}>")
|
|
291
|
-
elif struct_type == 'map':
|
|
292
|
-
values_type = self.convert_structure_type_to_java(class_name, field_name, structure_type.get('values', {'type': 'any'}), parent_package, nullable=True).type_name
|
|
293
|
-
return StructureToJava.JavaType(f"Map<String,{values_type}>")
|
|
294
|
-
elif struct_type == 'choice':
|
|
295
|
-
return self.generate_choice(structure_type, parent_package, write_file=True)
|
|
296
|
-
elif struct_type == 'tuple':
|
|
297
|
-
return self.generate_tuple(structure_type, parent_package, write_file=True)
|
|
298
|
-
else:
|
|
299
|
-
return self.convert_structure_type_to_java(class_name, field_name, struct_type, parent_package)
|
|
300
|
-
return StructureToJava.JavaType('Object')
|
|
301
|
-
|
|
302
|
-
def generate_class_or_enum(self, structure_schema: Dict, parent_package: str, write_file: bool = True, explicit_name: str = '') -> JavaType:
|
|
303
|
-
""" Generates a Java class or enum from a JSON Structure schema """
|
|
304
|
-
struct_type = structure_schema.get('type', 'object')
|
|
305
|
-
if 'enum' in structure_schema:
|
|
306
|
-
return self.generate_enum(structure_schema, explicit_name or structure_schema.get('name', 'UnnamedEnum'), parent_package, write_file)
|
|
307
|
-
elif struct_type == 'object':
|
|
308
|
-
return self.generate_class(structure_schema, parent_package, write_file, explicit_name=explicit_name)
|
|
309
|
-
elif struct_type == 'choice':
|
|
310
|
-
return self.generate_choice(structure_schema, parent_package, write_file, explicit_name=explicit_name)
|
|
311
|
-
elif struct_type == 'tuple':
|
|
312
|
-
return self.generate_tuple(structure_schema, parent_package, write_file, explicit_name=explicit_name)
|
|
313
|
-
return StructureToJava.JavaType('Object')
|
|
314
|
-
|
|
315
|
-
def generate_class(self, structure_schema: Dict, parent_package: str, write_file: bool, explicit_name: str = '') -> JavaType:
|
|
316
|
-
""" Generates a Java class from a JSON Structure object schema """
|
|
317
|
-
|
|
318
|
-
# Get name and namespace
|
|
319
|
-
class_name = pascal(explicit_name if explicit_name else structure_schema.get('name', 'UnnamedClass'))
|
|
320
|
-
schema_namespace = structure_schema.get('namespace', parent_package)
|
|
321
|
-
if not 'namespace' in structure_schema:
|
|
322
|
-
structure_schema['namespace'] = schema_namespace
|
|
323
|
-
package = self.join_packages(self.base_package, schema_namespace).replace('.', '/').lower()
|
|
324
|
-
package = package.replace('.', '/').lower()
|
|
325
|
-
package = self.safe_package(package)
|
|
326
|
-
class_name = self.safe_identifier(class_name)
|
|
327
|
-
namespace_qualified_name = self.qualified_name(schema_namespace, explicit_name or structure_schema.get('name', 'UnnamedClass'))
|
|
328
|
-
qualified_class_name = self.qualified_name(package.replace('/', '.'), class_name)
|
|
329
|
-
if namespace_qualified_name in self.generated_types_structure_namespace:
|
|
330
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
331
|
-
self.generated_types_structure_namespace[namespace_qualified_name] = "class"
|
|
332
|
-
self.generated_types_java_package[qualified_class_name] = "class"
|
|
333
|
-
|
|
334
|
-
# Check if this is an abstract type
|
|
335
|
-
is_abstract = structure_schema.get('abstract', False)
|
|
336
|
-
deprecated = structure_schema.get('deprecated', False)
|
|
337
|
-
|
|
338
|
-
# Generate documentation
|
|
339
|
-
doc = structure_schema.get('description', structure_schema.get('doc', class_name))
|
|
340
|
-
|
|
341
|
-
# Handle inheritance
|
|
342
|
-
base_class = None
|
|
343
|
-
if '$extends' in structure_schema:
|
|
344
|
-
base_ref = structure_schema['$extends']
|
|
345
|
-
base_schema = self.resolve_ref(base_ref, self.schema_doc if isinstance(self.schema_doc, dict) else None)
|
|
346
|
-
if base_schema:
|
|
347
|
-
ref_path = base_ref.split('/')
|
|
348
|
-
base_name = ref_path[-1]
|
|
349
|
-
ref_namespace = '.'.join(ref_path[2:-1]) if len(ref_path) > 3 else parent_package
|
|
350
|
-
base_type = self.generate_class(base_schema, ref_namespace, write_file=True, explicit_name=base_name)
|
|
351
|
-
base_class = base_type.type_name
|
|
352
|
-
|
|
353
|
-
# Generate field information for template
|
|
354
|
-
fields = []
|
|
355
|
-
for prop_name, prop_schema in structure_schema.get('properties', {}).items():
|
|
356
|
-
field_info = self.generate_field_info(class_name, prop_name, prop_schema, schema_namespace)
|
|
357
|
-
fields.append(field_info)
|
|
358
|
-
|
|
359
|
-
# Use template for class generation
|
|
360
|
-
class_definition = process_template(
|
|
361
|
-
"structuretojava/class_core.jinja",
|
|
362
|
-
class_name=class_name,
|
|
363
|
-
docstring=doc,
|
|
364
|
-
is_abstract=is_abstract,
|
|
365
|
-
deprecated=deprecated,
|
|
366
|
-
base_class=base_class,
|
|
367
|
-
fields=fields,
|
|
368
|
-
jackson_annotation=self.jackson_annotations
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
# Generate Equals and GetHashCode using template
|
|
372
|
-
properties = structure_schema.get('properties', {})
|
|
373
|
-
non_const_properties = {k: v for k, v in properties.items() if 'const' not in v}
|
|
374
|
-
field_names = []
|
|
375
|
-
for prop_name in non_const_properties.keys():
|
|
376
|
-
field_name_java = pascal(prop_name) if self.pascal_properties else prop_name
|
|
377
|
-
safe_field_name = self.safe_identifier(field_name_java, class_name)
|
|
378
|
-
field_names.append(safe_field_name)
|
|
379
|
-
|
|
380
|
-
equals_hashcode = process_template(
|
|
381
|
-
"structuretojava/equals_hashcode.jinja",
|
|
382
|
-
class_name=class_name,
|
|
383
|
-
fields=field_names,
|
|
384
|
-
field_count=len(field_names)
|
|
385
|
-
)
|
|
386
|
-
|
|
387
|
-
class_definition = class_definition.rstrip() + equals_hashcode + "\n}\n"
|
|
388
|
-
|
|
389
|
-
if write_file:
|
|
390
|
-
self.write_to_file(package, class_name, class_definition)
|
|
391
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
392
|
-
|
|
393
|
-
def generate_field_info(self, class_name: str, prop_name: str, prop_schema: Dict, parent_package: str) -> Dict:
|
|
394
|
-
""" Generates field information for template """
|
|
395
|
-
field_name_java = pascal(prop_name) if self.pascal_properties else prop_name
|
|
396
|
-
field_type = self.convert_structure_type_to_java(class_name, prop_name, prop_schema, parent_package)
|
|
397
|
-
safe_field_name = self.safe_identifier(field_name_java, class_name)
|
|
398
|
-
|
|
399
|
-
# Generate documentation
|
|
400
|
-
doc = prop_schema.get('description', prop_schema.get('doc', field_name_java))
|
|
401
|
-
|
|
402
|
-
# Check if this is a const field
|
|
403
|
-
is_const = 'const' in prop_schema
|
|
404
|
-
const_value = None
|
|
405
|
-
if is_const:
|
|
406
|
-
const_val = prop_schema['const']
|
|
407
|
-
prop_type = field_type.type_name
|
|
408
|
-
if prop_type.endswith('?'):
|
|
409
|
-
prop_type = prop_type[:-1]
|
|
410
|
-
const_value = self.format_const_value(const_val, prop_type)
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
'name': safe_field_name,
|
|
414
|
-
'original_name': prop_name,
|
|
415
|
-
'type': field_type.type_name,
|
|
416
|
-
'docstring': doc,
|
|
417
|
-
'is_const': is_const,
|
|
418
|
-
'const_value': const_value
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
def generate_property(self, class_name: str, field: Tuple[str, Dict], parent_package: str) -> str:
|
|
422
|
-
""" Generates a Java property definition (legacy method for compatibility) """
|
|
423
|
-
field_name, field_schema = field
|
|
424
|
-
field_info = self.generate_field_info(class_name, field_name, field_schema, parent_package)
|
|
425
|
-
|
|
426
|
-
property_def = f"{INDENT}/** {field_info['docstring']} */\n"
|
|
427
|
-
|
|
428
|
-
if self.jackson_annotations and field_info['original_name'] != field_info['name']:
|
|
429
|
-
property_def += f'{INDENT}@JsonProperty("{field_info["original_name"]}")\n'
|
|
430
|
-
|
|
431
|
-
if field_info['is_const']:
|
|
432
|
-
property_def += f"{INDENT}public static final {field_info['type']} {field_info['name']} = {field_info['const_value']};\n"
|
|
433
|
-
else:
|
|
434
|
-
property_def += f"{INDENT}private {field_info['type']} {field_info['name']};\n"
|
|
435
|
-
property_def += f"{INDENT}public {field_info['type']} get{pascal(field_info['name'])}() {{ return {field_info['name']}; }}\n"
|
|
436
|
-
property_def += f"{INDENT}public void set{pascal(field_info['name'])}({field_info['type']} {field_info['name']}) {{ this.{field_info['name']} = {field_info['name']}; }}\n"
|
|
437
|
-
|
|
438
|
-
return property_def
|
|
439
|
-
|
|
440
|
-
def format_const_value(self, value: Any, java_type: str) -> str:
|
|
441
|
-
""" Formats a constant value for Java """
|
|
442
|
-
if value is None:
|
|
443
|
-
return "null"
|
|
444
|
-
elif isinstance(value, bool):
|
|
445
|
-
return "true" if value else "false"
|
|
446
|
-
elif isinstance(value, str):
|
|
447
|
-
return f'"{value}"'
|
|
448
|
-
elif isinstance(value, (int, float)):
|
|
449
|
-
if java_type in ['float', 'Float']:
|
|
450
|
-
return f"{value}f"
|
|
451
|
-
elif java_type in ['long', 'Long']:
|
|
452
|
-
return f"{value}L"
|
|
453
|
-
elif java_type in ['double', 'Double']:
|
|
454
|
-
return f"{value}d"
|
|
455
|
-
return str(value)
|
|
456
|
-
return f"/* unsupported const value */"
|
|
457
|
-
|
|
458
|
-
def generate_enum(self, structure_schema: Dict, field_name: str, parent_package: str, write_file: bool) -> JavaType:
|
|
459
|
-
""" Generates a Java enum from JSON Structure enum schema """
|
|
460
|
-
|
|
461
|
-
# Determine enum name
|
|
462
|
-
enum_name = pascal(structure_schema.get('name', field_name + 'Enum'))
|
|
463
|
-
schema_namespace = structure_schema.get('namespace', parent_package)
|
|
464
|
-
package = self.join_packages(self.base_package, schema_namespace).replace('.', '/').lower()
|
|
465
|
-
enum_name = self.safe_identifier(enum_name)
|
|
466
|
-
type_name = self.qualified_name(package.replace('/', '.'), enum_name)
|
|
467
|
-
namespace_qualified_name = self.qualified_name(schema_namespace, structure_schema.get('name', field_name + 'Enum'))
|
|
468
|
-
self.generated_types_structure_namespace[namespace_qualified_name] = "enum"
|
|
469
|
-
self.generated_types_java_package[type_name] = "enum"
|
|
470
|
-
|
|
471
|
-
# Get enum values
|
|
472
|
-
symbols = structure_schema.get('enum', [])
|
|
473
|
-
if not symbols:
|
|
474
|
-
return StructureToJava.JavaType('Object')
|
|
475
|
-
|
|
476
|
-
# Generate documentation
|
|
477
|
-
doc = structure_schema.get('description', structure_schema.get('doc', enum_name))
|
|
478
|
-
deprecated = structure_schema.get('deprecated', False)
|
|
479
|
-
|
|
480
|
-
# Determine base type
|
|
481
|
-
base_type = structure_schema.get('type', 'string')
|
|
482
|
-
numeric_types = ['int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64']
|
|
483
|
-
is_numeric = base_type in numeric_types
|
|
484
|
-
|
|
485
|
-
if is_numeric:
|
|
486
|
-
java_base_type = self.map_primitive_to_java(base_type, False).type_name
|
|
487
|
-
symbol_list = [{'name': f"VALUE_{value}".upper(), 'value': value} for value in symbols]
|
|
488
|
-
enum_definition = process_template(
|
|
489
|
-
"structuretojava/enum_core.jinja",
|
|
490
|
-
class_name=enum_name,
|
|
491
|
-
docstring=doc,
|
|
492
|
-
deprecated=deprecated,
|
|
493
|
-
is_numeric=True,
|
|
494
|
-
numeric_type=java_base_type,
|
|
495
|
-
symbols=symbol_list
|
|
496
|
-
)
|
|
497
|
-
else:
|
|
498
|
-
# String enum
|
|
499
|
-
safe_symbols = [self.safe_identifier(pascal(str(symbol).replace('-', '_').replace(' ', '_'))) for symbol in symbols]
|
|
500
|
-
enum_definition = process_template(
|
|
501
|
-
"structuretojava/enum_core.jinja",
|
|
502
|
-
class_name=enum_name,
|
|
503
|
-
docstring=doc,
|
|
504
|
-
deprecated=deprecated,
|
|
505
|
-
is_numeric=False,
|
|
506
|
-
symbols=safe_symbols
|
|
507
|
-
)
|
|
508
|
-
|
|
509
|
-
if write_file:
|
|
510
|
-
self.write_to_file(package, enum_name, enum_definition)
|
|
511
|
-
return StructureToJava.JavaType(type_name, is_enum=True)
|
|
512
|
-
|
|
513
|
-
def generate_choice(self, structure_schema: Dict, parent_package: str, write_file: bool, explicit_name: str = '') -> JavaType:
|
|
514
|
-
""" Generates a choice (discriminated union) type """
|
|
515
|
-
class_name = pascal(explicit_name if explicit_name else structure_schema.get('name', 'UnnamedChoice'))
|
|
516
|
-
schema_namespace = structure_schema.get('namespace', parent_package)
|
|
517
|
-
package = self.join_packages(self.base_package, schema_namespace).replace('.', '/').lower()
|
|
518
|
-
package = self.safe_package(package)
|
|
519
|
-
class_name = self.safe_identifier(class_name)
|
|
520
|
-
namespace_qualified_name = self.qualified_name(schema_namespace, explicit_name or structure_schema.get('name', 'UnnamedChoice'))
|
|
521
|
-
qualified_class_name = self.qualified_name(package.replace('/', '.'), class_name)
|
|
522
|
-
|
|
523
|
-
if namespace_qualified_name in self.generated_types_structure_namespace:
|
|
524
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
525
|
-
|
|
526
|
-
self.generated_types_structure_namespace[namespace_qualified_name] = "class"
|
|
527
|
-
self.generated_types_java_package[qualified_class_name] = "class"
|
|
528
|
-
|
|
529
|
-
# Get choice definitions
|
|
530
|
-
choices_dict = structure_schema.get('choices', {})
|
|
531
|
-
doc = structure_schema.get('description', structure_schema.get('doc', class_name))
|
|
532
|
-
deprecated = structure_schema.get('deprecated', False)
|
|
533
|
-
|
|
534
|
-
# Build choice information for template
|
|
535
|
-
choices = []
|
|
536
|
-
for choice_name, choice_schema in choices_dict.items():
|
|
537
|
-
choice_type_name = pascal(choice_name)
|
|
538
|
-
value_type = self.convert_structure_type_to_java(class_name, choice_name, choice_schema, schema_namespace)
|
|
539
|
-
choices.append({
|
|
540
|
-
'name': choice_name,
|
|
541
|
-
'type': choice_type_name,
|
|
542
|
-
'value_type': value_type.type_name,
|
|
543
|
-
'docstring': choice_schema.get('description', choice_schema.get('doc', f'{choice_name} variant'))
|
|
544
|
-
})
|
|
545
|
-
|
|
546
|
-
# Use template for choice generation
|
|
547
|
-
choice_definition = process_template(
|
|
548
|
-
"structuretojava/choice_core.jinja",
|
|
549
|
-
class_name=class_name,
|
|
550
|
-
docstring=doc,
|
|
551
|
-
deprecated=deprecated,
|
|
552
|
-
choices=choices,
|
|
553
|
-
jackson_annotation=self.jackson_annotations
|
|
554
|
-
)
|
|
555
|
-
|
|
556
|
-
if write_file:
|
|
557
|
-
self.write_to_file(package, class_name, choice_definition)
|
|
558
|
-
|
|
559
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
560
|
-
|
|
561
|
-
def generate_tuple(self, structure_schema: Dict, parent_package: str, write_file: bool, explicit_name: str = '') -> JavaType:
|
|
562
|
-
""" Generates a tuple type - Per JSON Structure spec, tuples serialize as JSON arrays """
|
|
563
|
-
class_name = pascal(explicit_name if explicit_name else structure_schema.get('name', 'UnnamedTuple'))
|
|
564
|
-
schema_namespace = structure_schema.get('namespace', parent_package)
|
|
565
|
-
package = self.join_packages(self.base_package, schema_namespace).replace('.', '/').lower()
|
|
566
|
-
package = self.safe_package(package)
|
|
567
|
-
class_name = self.safe_identifier(class_name)
|
|
568
|
-
namespace_qualified_name = self.qualified_name(schema_namespace, explicit_name or structure_schema.get('name', 'UnnamedTuple'))
|
|
569
|
-
qualified_class_name = self.qualified_name(package.replace('/', '.'), class_name)
|
|
570
|
-
|
|
571
|
-
if namespace_qualified_name in self.generated_types_structure_namespace:
|
|
572
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
573
|
-
|
|
574
|
-
self.generated_types_structure_namespace[namespace_qualified_name] = "class"
|
|
575
|
-
self.generated_types_java_package[qualified_class_name] = "class"
|
|
576
|
-
|
|
577
|
-
# Get tuple order and properties
|
|
578
|
-
properties = structure_schema.get('properties', {})
|
|
579
|
-
tuple_order = structure_schema.get('tuple', [])
|
|
580
|
-
doc = structure_schema.get('description', structure_schema.get('doc', class_name))
|
|
581
|
-
deprecated = structure_schema.get('deprecated', False)
|
|
582
|
-
|
|
583
|
-
# Build tuple element information in correct order
|
|
584
|
-
elements = []
|
|
585
|
-
for prop_name in tuple_order:
|
|
586
|
-
if prop_name in properties:
|
|
587
|
-
prop_schema = properties[prop_name]
|
|
588
|
-
prop_type = self.convert_structure_type_to_java(class_name, prop_name, prop_schema, schema_namespace)
|
|
589
|
-
field_name = pascal(prop_name) if self.pascal_properties else prop_name
|
|
590
|
-
safe_field_name = self.safe_identifier(field_name, class_name)
|
|
591
|
-
elements.append({
|
|
592
|
-
'name': safe_field_name,
|
|
593
|
-
'type': prop_type.type_name,
|
|
594
|
-
'docstring': prop_schema.get('description', prop_schema.get('doc', prop_name))
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
# Use template for tuple generation
|
|
598
|
-
tuple_definition = process_template(
|
|
599
|
-
"structuretojava/tuple_core.jinja",
|
|
600
|
-
class_name=class_name,
|
|
601
|
-
docstring=doc,
|
|
602
|
-
deprecated=deprecated,
|
|
603
|
-
elements=elements,
|
|
604
|
-
jackson_annotation=self.jackson_annotations
|
|
605
|
-
)
|
|
606
|
-
|
|
607
|
-
if write_file:
|
|
608
|
-
self.write_to_file(package, class_name, tuple_definition)
|
|
609
|
-
|
|
610
|
-
return StructureToJava.JavaType(qualified_class_name, is_class=True)
|
|
611
|
-
|
|
612
|
-
def generate_embedded_union_class_jackson(self, class_name: str, field_name: str, structure_types: List, parent_package: str, write_file: bool) -> str:
|
|
613
|
-
""" Generates an embedded Union Class for Java using Jackson """
|
|
614
|
-
# Simplified implementation - just return Object for unions
|
|
615
|
-
return 'Object'
|
|
616
|
-
|
|
617
|
-
def generate_equals_and_gethashcode(self, structure_schema: Dict, class_name: str, parent_namespace: str) -> str:
|
|
618
|
-
""" Generates Equals and GetHashCode methods """
|
|
619
|
-
code = "\n"
|
|
620
|
-
properties = structure_schema.get('properties', {})
|
|
621
|
-
|
|
622
|
-
# Filter out const properties
|
|
623
|
-
non_const_properties = {k: v for k, v in properties.items() if 'const' not in v}
|
|
624
|
-
|
|
625
|
-
if not non_const_properties:
|
|
626
|
-
# Empty class
|
|
627
|
-
code += f"{INDENT}@Override\n{INDENT}public boolean equals(Object obj) {{\n"
|
|
628
|
-
code += f"{INDENT*2}return obj instanceof {class_name};\n"
|
|
629
|
-
code += f"{INDENT}}}\n\n"
|
|
630
|
-
code += f"{INDENT}@Override\n{INDENT}public int hashCode() {{\n"
|
|
631
|
-
code += f"{INDENT*2}return 0;\n"
|
|
632
|
-
code += f"{INDENT}}}\n"
|
|
633
|
-
return code
|
|
634
|
-
|
|
635
|
-
# Generate equals
|
|
636
|
-
code += f"{INDENT}@Override\n{INDENT}public boolean equals(Object obj) {{\n"
|
|
637
|
-
code += f"{INDENT*2}if (this == obj) return true;\n"
|
|
638
|
-
code += f"{INDENT*2}if (!(obj instanceof {class_name})) return false;\n"
|
|
639
|
-
code += f"{INDENT*2}{class_name} other = ({class_name}) obj;\n"
|
|
640
|
-
|
|
641
|
-
equality_checks = []
|
|
642
|
-
for prop_name in non_const_properties.keys():
|
|
643
|
-
field_name_java = pascal(prop_name) if self.pascal_properties else prop_name
|
|
644
|
-
safe_field_name = self.safe_identifier(field_name_java, class_name)
|
|
645
|
-
equality_checks.append(f"Objects.equals(this.{safe_field_name}, other.{safe_field_name})")
|
|
646
|
-
|
|
647
|
-
if len(equality_checks) == 1:
|
|
648
|
-
code += f"{INDENT*2}return {equality_checks[0]};\n"
|
|
649
|
-
else:
|
|
650
|
-
code += f"{INDENT*2}return " + f"\n{INDENT*3}&& ".join(equality_checks) + ";\n"
|
|
651
|
-
|
|
652
|
-
code += f"{INDENT}}}\n\n"
|
|
653
|
-
|
|
654
|
-
# Generate hashCode
|
|
655
|
-
code += f"{INDENT}@Override\n{INDENT}public int hashCode() {{\n"
|
|
656
|
-
|
|
657
|
-
hash_fields = []
|
|
658
|
-
for prop_name in non_const_properties.keys():
|
|
659
|
-
field_name_java = pascal(prop_name) if self.pascal_properties else prop_name
|
|
660
|
-
safe_field_name = self.safe_identifier(field_name_java, class_name)
|
|
661
|
-
hash_fields.append(safe_field_name)
|
|
662
|
-
|
|
663
|
-
if len(hash_fields) <= 8:
|
|
664
|
-
code += f"{INDENT*2}return Objects.hash({', '.join(hash_fields)});\n"
|
|
665
|
-
else:
|
|
666
|
-
code += f"{INDENT*2}int result = Objects.hash({', '.join(hash_fields[:8])});\n"
|
|
667
|
-
for i in range(8, len(hash_fields)):
|
|
668
|
-
code += f"{INDENT*2}result = 31 * result + Objects.hashCode({hash_fields[i]});\n"
|
|
669
|
-
code += f"{INDENT*2}return result;\n"
|
|
670
|
-
|
|
671
|
-
code += f"{INDENT}}}\n"
|
|
672
|
-
|
|
673
|
-
return code
|
|
674
|
-
|
|
675
|
-
def write_to_file(self, package: str, name: str, definition: str):
|
|
676
|
-
""" Writes a Java class or enum to a file """
|
|
677
|
-
package = package.lower()
|
|
678
|
-
package = self.safe_package(package)
|
|
679
|
-
directory_path = os.path.join(
|
|
680
|
-
self.output_dir, package.replace('.', os.sep).replace('/', os.sep))
|
|
681
|
-
if not os.path.exists(directory_path):
|
|
682
|
-
os.makedirs(directory_path, exist_ok=True)
|
|
683
|
-
file_path = os.path.join(directory_path, f"{name}.java")
|
|
684
|
-
|
|
685
|
-
with open(file_path, 'w', encoding='utf-8') as file:
|
|
686
|
-
if package:
|
|
687
|
-
file.write(f"package {package.replace('/', '.')};\n\n")
|
|
688
|
-
if "List<" in definition:
|
|
689
|
-
file.write("import java.util.List;\n")
|
|
690
|
-
if "Set<" in definition:
|
|
691
|
-
file.write("import java.util.Set;\n")
|
|
692
|
-
if "Map<" in definition:
|
|
693
|
-
file.write("import java.util.Map;\n")
|
|
694
|
-
if "BigDecimal" in definition:
|
|
695
|
-
file.write("import java.math.BigDecimal;\n")
|
|
696
|
-
if "BigInteger" in definition:
|
|
697
|
-
file.write("import java.math.BigInteger;\n")
|
|
698
|
-
if "LocalDate" in definition:
|
|
699
|
-
file.write("import java.time.LocalDate;\n")
|
|
700
|
-
if "LocalTime" in definition:
|
|
701
|
-
file.write("import java.time.LocalTime;\n")
|
|
702
|
-
if "Instant" in definition:
|
|
703
|
-
file.write("import java.time.Instant;\n")
|
|
704
|
-
if "LocalDateTime" in definition:
|
|
705
|
-
file.write("import java.time.LocalDateTime;\n")
|
|
706
|
-
if "UUID" in definition:
|
|
707
|
-
file.write("import java.util.UUID;\n")
|
|
708
|
-
if "Duration" in definition:
|
|
709
|
-
file.write("import java.time.Duration;\n")
|
|
710
|
-
if "URI" in definition:
|
|
711
|
-
file.write("import java.net.URI;\n")
|
|
712
|
-
if "Objects" in definition:
|
|
713
|
-
file.write("import java.util.Objects;\n")
|
|
714
|
-
|
|
715
|
-
if self.jackson_annotations:
|
|
716
|
-
if 'JsonProperty' in definition:
|
|
717
|
-
file.write("import com.fasterxml.jackson.annotation.JsonProperty;\n")
|
|
718
|
-
if 'JsonNode' in definition:
|
|
719
|
-
file.write("import com.fasterxml.jackson.databind.JsonNode;\n")
|
|
720
|
-
if 'ObjectMapper' in definition:
|
|
721
|
-
file.write("import com.fasterxml.jackson.databind.ObjectMapper;\n")
|
|
722
|
-
if 'JsonSerialize' in definition:
|
|
723
|
-
file.write("import com.fasterxml.jackson.databind.annotation.JsonSerialize;\n")
|
|
724
|
-
if 'JsonDeserialize' in definition:
|
|
725
|
-
file.write("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n")
|
|
726
|
-
if 'JsonSerializer' in definition:
|
|
727
|
-
file.write("import com.fasterxml.jackson.databind.JsonSerializer;\n")
|
|
728
|
-
if 'SerializerProvider' in definition:
|
|
729
|
-
file.write("import com.fasterxml.jackson.databind.SerializerProvider;\n")
|
|
730
|
-
if 'JsonDeserializer' in definition:
|
|
731
|
-
file.write("import com.fasterxml.jackson.databind.JsonDeserializer;\n")
|
|
732
|
-
if 'DeserializationContext' in definition:
|
|
733
|
-
file.write("import com.fasterxml.jackson.databind.DeserializationContext;\n")
|
|
734
|
-
if 'JsonParser' in definition:
|
|
735
|
-
file.write("import com.fasterxml.jackson.core.JsonParser;\n")
|
|
736
|
-
if 'JsonIgnore' in definition:
|
|
737
|
-
file.write("import com.fasterxml.jackson.annotation.JsonIgnore;\n")
|
|
738
|
-
if 'JsonProcessingException' in definition:
|
|
739
|
-
file.write("import com.fasterxml.jackson.core.JsonProcessingException;\n")
|
|
740
|
-
if 'JsonGenerator' in definition:
|
|
741
|
-
file.write("import com.fasterxml.jackson.core.JsonGenerator;\n")
|
|
742
|
-
if 'TypeReference' in definition:
|
|
743
|
-
file.write("import com.fasterxml.jackson.core.type.TypeReference;\n")
|
|
744
|
-
if 'JsonFormat' in definition:
|
|
745
|
-
file.write("import com.fasterxml.jackson.annotation.JsonFormat;\n")
|
|
746
|
-
if 'JsonCreator' in definition:
|
|
747
|
-
file.write("import com.fasterxml.jackson.annotation.JsonCreator;\n")
|
|
748
|
-
if 'JsonValue' in definition:
|
|
749
|
-
file.write("import com.fasterxml.jackson.annotation.JsonValue;\n")
|
|
750
|
-
if 'JsonTypeInfo' in definition:
|
|
751
|
-
file.write("import com.fasterxml.jackson.annotation.JsonTypeInfo;\n")
|
|
752
|
-
if 'JsonSubTypes' in definition:
|
|
753
|
-
file.write("import com.fasterxml.jackson.annotation.JsonSubTypes;\n")
|
|
754
|
-
file.write("import com.fasterxml.jackson.core.JsonGenerator;\n")
|
|
755
|
-
if 'TypeReference' in definition:
|
|
756
|
-
file.write("import com.fasterxml.jackson.core.type.TypeReference;\n")
|
|
757
|
-
file.write("\n")
|
|
758
|
-
file.write(definition)
|
|
759
|
-
|
|
760
|
-
def convert_schema(self, schema: JsonNode, output_dir: str):
|
|
761
|
-
"""Converts JSON Structure schema to Java"""
|
|
762
|
-
if not isinstance(schema, list):
|
|
763
|
-
schema = [schema]
|
|
764
|
-
if not os.path.exists(output_dir):
|
|
765
|
-
os.makedirs(output_dir, exist_ok=True)
|
|
766
|
-
pom_path = os.path.join(output_dir, "pom.xml")
|
|
767
|
-
if not os.path.exists(pom_path):
|
|
768
|
-
package_elements = self.base_package.split('.') if self.base_package else ["com", "example"]
|
|
769
|
-
groupid = '.'.join(package_elements[:-1]) if len(package_elements) > 1 else package_elements[0]
|
|
770
|
-
artifactid = package_elements[-1]
|
|
771
|
-
pom_content = process_template(
|
|
772
|
-
"structuretojava/pom.xml.jinja",
|
|
773
|
-
groupid=groupid,
|
|
774
|
-
artifactid=artifactid,
|
|
775
|
-
jackson_version=JACKSON_VERSION
|
|
776
|
-
)
|
|
777
|
-
with open(pom_path, 'w', encoding='utf-8') as file:
|
|
778
|
-
file.write(pom_content)
|
|
779
|
-
output_dir = os.path.join(
|
|
780
|
-
output_dir, "src/main/java".replace('/', os.sep))
|
|
781
|
-
if not os.path.exists(output_dir):
|
|
782
|
-
os.makedirs(output_dir, exist_ok=True)
|
|
783
|
-
self.output_dir = output_dir
|
|
784
|
-
|
|
785
|
-
# Register all schemas with $id keywords
|
|
786
|
-
for structure_schema in (x for x in schema if isinstance(x, dict)):
|
|
787
|
-
self.schema_doc = structure_schema
|
|
788
|
-
self.register_schema_ids(structure_schema)
|
|
789
|
-
|
|
790
|
-
# Generate classes
|
|
791
|
-
for structure_schema in (x for x in schema if isinstance(x, dict)):
|
|
792
|
-
self.schema_doc = structure_schema
|
|
793
|
-
if 'definitions' in structure_schema:
|
|
794
|
-
self.process_definitions(structure_schema['definitions'], '')
|
|
795
|
-
if 'type' in structure_schema or 'enum' in structure_schema:
|
|
796
|
-
self.generate_class_or_enum(structure_schema, '')
|
|
797
|
-
|
|
798
|
-
def process_definitions(self, definitions: Dict, namespace_path: str) -> None:
|
|
799
|
-
""" Processes the definitions section recursively """
|
|
800
|
-
for name, definition in definitions.items():
|
|
801
|
-
if isinstance(definition, dict):
|
|
802
|
-
if 'type' in definition or 'enum' in definition:
|
|
803
|
-
# This is a type definition
|
|
804
|
-
current_namespace = namespace_path
|
|
805
|
-
self.generate_class_or_enum(definition, current_namespace, write_file=True, explicit_name=name)
|
|
806
|
-
else:
|
|
807
|
-
# This might be a nested namespace
|
|
808
|
-
new_namespace = f"{namespace_path}.{name}" if namespace_path else name
|
|
809
|
-
self.process_definitions(definition, new_namespace)
|
|
810
|
-
|
|
811
|
-
def convert(self, structure_schema_path: str, output_dir: str):
|
|
812
|
-
"""Converts JSON Structure schema to Java"""
|
|
813
|
-
with open(structure_schema_path, 'r', encoding='utf-8') as file:
|
|
814
|
-
schema = json.load(file)
|
|
815
|
-
self.convert_schema(schema, output_dir)
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
def convert_structure_to_java(structure_schema_path, java_file_path, package_name='', pascal_properties=False, jackson_annotation=True):
|
|
819
|
-
"""
|
|
820
|
-
Converts JSON Structure schema to Java classes
|
|
821
|
-
|
|
822
|
-
Args:
|
|
823
|
-
structure_schema_path: JSON Structure input schema path
|
|
824
|
-
java_file_path: Output Java directory path
|
|
825
|
-
package_name: Base package name
|
|
826
|
-
pascal_properties: Use PascalCase for properties
|
|
827
|
-
jackson_annotation: Add Jackson annotations (always True for Structure)
|
|
828
|
-
"""
|
|
829
|
-
if not package_name:
|
|
830
|
-
package_name = os.path.splitext(os.path.basename(java_file_path))[0].replace('-', '_').lower()
|
|
831
|
-
structuretojava = StructureToJava()
|
|
832
|
-
structuretojava.base_package = package_name
|
|
833
|
-
structuretojava.pascal_properties = pascal_properties
|
|
834
|
-
structuretojava.jackson_annotations = jackson_annotation
|
|
835
|
-
structuretojava.convert(structure_schema_path, java_file_path)
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
def convert_structure_schema_to_java(structure_schema: JsonNode, output_dir: str, package_name='', pascal_properties=False, jackson_annotation=True):
|
|
839
|
-
"""
|
|
840
|
-
Converts JSON Structure schema to Java classes
|
|
841
|
-
|
|
842
|
-
Args:
|
|
843
|
-
structure_schema: JSON Structure schema as a dictionary or list of dictionaries
|
|
844
|
-
output_dir: Output directory path
|
|
845
|
-
package_name: Base package name
|
|
846
|
-
pascal_properties: Use PascalCase for properties
|
|
847
|
-
jackson_annotation: Add Jackson annotations (always True for Structure)
|
|
848
|
-
"""
|
|
849
|
-
structuretojava = StructureToJava()
|
|
850
|
-
structuretojava.base_package = package_name
|
|
851
|
-
structuretojava.pascal_properties = pascal_properties
|
|
852
|
-
structuretojava.jackson_annotations = jackson_annotation
|
|
853
|
-
structuretojava.convert_schema(structure_schema, output_dir)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|