Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/schema_salad/java_codegen.py @ 2:6af9afd405e9 draft
"planemo upload commit 0a63dd5f4d38a1f6944587f52a8cd79874177fc1"
author | shellac |
---|---|
date | Thu, 14 May 2020 14:56:58 -0400 |
parents | 26e78fe6e8c4 |
children |
comparison
equal
deleted
inserted
replaced
1:75ca89e9b81c | 2:6af9afd405e9 |
---|---|
1 """Work-in-progress Java code generator for a given schema salad definition.""" | |
2 import os | |
3 import pkg_resources | |
4 import string | |
5 import shutil | |
6 from io import open as io_open | |
7 from typing import Any, Dict, List, MutableMapping, MutableSequence, Optional, Union | |
8 | |
9 from six import iteritems, itervalues | |
10 from six.moves import cStringIO, urllib | |
11 from typing_extensions import Text # pylint: disable=unused-import | |
12 | |
13 from . import schema | |
14 from .codegen_base import CodeGenBase, TypeDef | |
15 from .exceptions import SchemaException | |
16 from .schema import shortname | |
17 | |
18 # move to a regular typing import when Python 3.3-3.6 is no longer supported | |
19 | |
20 # experiment at providing more typed objects building a optional type that allows | |
21 # referencing one or a list of objects. It is useful for improving the RootLoader | |
22 # for simple schema with a single root loader - but doesn't help with CWL at all and | |
23 # may even confuse things a bit so turning these off be default. | |
24 USE_ONE_OR_LIST_OF_TYPES = False | |
25 | |
26 | |
27 def _ensure_directory_and_write(path, contents): | |
28 # type: (Text, Text) -> None | |
29 dirname = os.path.dirname(path) | |
30 _safe_makedirs(dirname) | |
31 with io_open(path, mode="w", encoding="utf-8") as f: | |
32 f.write(contents) | |
33 | |
34 | |
35 def doc_to_doc_string(doc, indent_level=0): | |
36 # type: (Text, int) -> Text | |
37 lead = " " + " " * indent_level + "* " * indent_level | |
38 if doc: | |
39 doc_str = "{}<BLOCKQUOTE>\n".format(lead) | |
40 doc_str += "\n".join(["{}{}".format(lead, l) for l in doc.split("\n")]) | |
41 doc_str += "{}</BLOCKQUOTE>".format(lead) | |
42 else: | |
43 doc_str = "" | |
44 return doc_str | |
45 | |
46 | |
47 def _safe_makedirs(path): | |
48 # type: (Text) -> None | |
49 if not os.path.exists(path): | |
50 os.makedirs(path) | |
51 | |
52 | |
53 class JavaCodeGen(CodeGenBase): | |
54 def __init__(self, base, target, examples): | |
55 # type: (Text, Optional[str], Optional[str]) -> None | |
56 super(JavaCodeGen, self).__init__() | |
57 self.base_uri = base | |
58 sp = urllib.parse.urlsplit(base) | |
59 self.examples = examples | |
60 self.package = ".".join( | |
61 list(reversed(sp.netloc.split("."))) + sp.path.strip("/").split("/") | |
62 ) | |
63 self.artifact = self.package.split(".")[-1] | |
64 target = target or "." | |
65 self.target_dir = target | |
66 rel_package_dir = self.package.replace(".", "/") | |
67 self.rel_package_dir = rel_package_dir | |
68 self.main_src_dir = os.path.join( | |
69 self.target_dir, "src", "main", "java", rel_package_dir | |
70 ) | |
71 self.test_src_dir = os.path.join( | |
72 self.target_dir, "src", "test", "java", rel_package_dir | |
73 ) | |
74 self.test_resources_dir = os.path.join( | |
75 self.target_dir, "src", "test", "resources", rel_package_dir | |
76 ) | |
77 | |
78 def prologue(self): # type: () -> None | |
79 for src_dir in [self.main_src_dir, self.test_src_dir]: | |
80 _safe_makedirs(src_dir) | |
81 | |
82 for primitive in itervalues(self.prims): | |
83 self.declare_type(primitive) | |
84 | |
85 @staticmethod | |
86 def property_name(name): # type: (Text) -> Text | |
87 avn = schema.avro_name(name) | |
88 return avn | |
89 | |
90 @staticmethod | |
91 def safe_name(name): # type: (Text) -> Text | |
92 avn = JavaCodeGen.property_name(name) | |
93 if avn in ("class", "extends", "abstract", "default", "package"): | |
94 # reserved words | |
95 avn = avn + "_" | |
96 return avn | |
97 | |
98 def interface_name(self, n): | |
99 # type: (Text) -> Text | |
100 return self.safe_name(n) | |
101 | |
102 def begin_class( | |
103 self, | |
104 classname, # type: Text | |
105 extends, # type: MutableSequence[Text] | |
106 doc, # type: Text | |
107 abstract, # type: bool | |
108 field_names, # type: MutableSequence[Text] | |
109 idfield, # type: Text | |
110 ): # type: (...) -> None | |
111 cls = self.interface_name(classname) | |
112 self.current_class = cls | |
113 self.current_class_is_abstract = abstract | |
114 self.current_loader = cStringIO() | |
115 self.current_fieldtypes = {} # type: Dict[Text, TypeDef] | |
116 self.current_fields = cStringIO() | |
117 interface_doc_str = u"* Auto-generated interface for <I>%s</I><BR>" % classname | |
118 if not abstract: | |
119 implemented_by = u"This interface is implemented by {{@link {}Impl}}<BR>" | |
120 interface_doc_str += implemented_by.format(cls) | |
121 interface_doc_str += doc_to_doc_string(doc) | |
122 class_doc_str = u"* Auto-generated class implementation for <I>{}</I><BR>".format( | |
123 classname | |
124 ) | |
125 class_doc_str += doc_to_doc_string(doc) | |
126 with open(os.path.join(self.main_src_dir, "{}.java".format(cls)), "w") as f: | |
127 | |
128 if extends: | |
129 ext = ( | |
130 "extends " | |
131 + ", ".join(self.interface_name(e) for e in extends) | |
132 + ", Savable" | |
133 ) | |
134 else: | |
135 ext = "extends Savable" | |
136 f.write( | |
137 """package {package}; | |
138 | |
139 import {package}.utils.Savable; | |
140 | |
141 /** | |
142 {interface_doc_str} | |
143 */ | |
144 public interface {cls} {ext} {{""".format( | |
145 package=self.package, | |
146 cls=cls, | |
147 ext=ext, | |
148 interface_doc_str=interface_doc_str, | |
149 ) | |
150 ) | |
151 | |
152 if self.current_class_is_abstract: | |
153 return | |
154 | |
155 with open(os.path.join(self.main_src_dir, "{}Impl.java".format(cls)), "w") as f: | |
156 f.write( | |
157 """package {package}; | |
158 | |
159 import {package}.utils.LoaderInstances; | |
160 import {package}.utils.LoadingOptions; | |
161 import {package}.utils.LoadingOptionsBuilder; | |
162 import {package}.utils.SavableImpl; | |
163 import {package}.utils.ValidationException; | |
164 | |
165 /** | |
166 {class_doc_str} | |
167 */ | |
168 public class {cls}Impl extends SavableImpl implements {cls} {{ | |
169 private LoadingOptions loadingOptions_ = new LoadingOptionsBuilder().build(); | |
170 private java.util.Map<String, Object> extensionFields_ = | |
171 new java.util.HashMap<String, Object>(); | |
172 """.format( | |
173 package=self.package, cls=cls, class_doc_str=class_doc_str | |
174 ) | |
175 ) | |
176 self.current_loader.write( | |
177 """ | |
178 /** | |
179 * Used by {{@link {package}.utils.RootLoader}} to construct instances of {cls}Impl. | |
180 * | |
181 * @param __doc_ Document fragment to load this record object from (presumably a | |
182 {{@link java.util.Map}}). | |
183 * @param __baseUri_ Base URI to generate child document IDs against. | |
184 * @param __loadingOptions Context for loading URIs and populating objects. | |
185 * @param __docRoot_ ID at this position in the document (if available) (maybe?) | |
186 * @throws ValidationException If the document fragment is not a {{@link java.util.Map}} | |
187 * or validation of fields fails. | |
188 */ | |
189 public {cls}Impl( | |
190 final Object __doc_, | |
191 final String __baseUri_, | |
192 LoadingOptions __loadingOptions, | |
193 final String __docRoot_) {{ | |
194 super(__doc_, __baseUri_, __loadingOptions, __docRoot_); | |
195 // Prefix plumbing variables with '__' to reduce likelihood of collision with | |
196 // generated names. | |
197 String __baseUri = __baseUri_; | |
198 String __docRoot = __docRoot_; | |
199 if (!(__doc_ instanceof java.util.Map)) {{ | |
200 throw new ValidationException("{cls}Impl called on non-map"); | |
201 }} | |
202 final java.util.Map<String, Object> __doc = (java.util.Map<String, Object>) __doc_; | |
203 final java.util.List<ValidationException> __errors = | |
204 new java.util.ArrayList<ValidationException>(); | |
205 if (__loadingOptions != null) {{ | |
206 this.loadingOptions_ = __loadingOptions; | |
207 }} | |
208 """.format( | |
209 cls=cls, package=self.package | |
210 ) | |
211 ) | |
212 | |
213 def end_class(self, classname, field_names): | |
214 # type: (Text, List[Text]) -> None | |
215 with open( | |
216 os.path.join(self.main_src_dir, "{}.java".format(self.current_class)), "a" | |
217 ) as f: | |
218 f.write( | |
219 """ | |
220 } | |
221 """ | |
222 ) | |
223 if self.current_class_is_abstract: | |
224 return | |
225 | |
226 self.current_loader.write( | |
227 """ if (!__errors.isEmpty()) { | |
228 throw new ValidationException("Trying 'RecordField'", __errors); | |
229 } | |
230 """ | |
231 ) | |
232 for fieldname in field_names: | |
233 fieldtype = self.current_fieldtypes.get(fieldname) | |
234 if fieldtype is None: | |
235 continue | |
236 self.current_loader.write( | |
237 """ this.{safename} = ({type}) {safename}; | |
238 """.format( | |
239 safename=self.safe_name(fieldname), type=fieldtype.instance_type | |
240 ) | |
241 ) | |
242 | |
243 self.current_loader.write(""" }""") | |
244 | |
245 with open( | |
246 os.path.join(self.main_src_dir, "{}Impl.java".format(self.current_class)), | |
247 "a", | |
248 ) as f: | |
249 f.write(self.current_fields.getvalue()) | |
250 f.write(self.current_loader.getvalue()) | |
251 f.write( | |
252 """ | |
253 } | |
254 """ | |
255 ) | |
256 | |
257 prims = { | |
258 u"http://www.w3.org/2001/XMLSchema#string": TypeDef( | |
259 instance_type="String", | |
260 init="new PrimitiveLoader<String>(String.class)", | |
261 name="StringInstance", | |
262 loader_type="Loader<String>", | |
263 ), | |
264 u"http://www.w3.org/2001/XMLSchema#int": TypeDef( | |
265 instance_type="Integer", | |
266 init="new PrimitiveLoader<Integer>(Integer.class)", | |
267 name="IntegerInstance", | |
268 loader_type="Loader<Integer>", | |
269 ), | |
270 u"http://www.w3.org/2001/XMLSchema#long": TypeDef( | |
271 instance_type="Long", | |
272 name="LongInstance", | |
273 loader_type="Loader<Long>", | |
274 init="new PrimitiveLoader<Long>(Long.class)", | |
275 ), | |
276 u"http://www.w3.org/2001/XMLSchema#float": TypeDef( | |
277 instance_type="Float", | |
278 name="FloatInstance", | |
279 loader_type="Loader<Float>", | |
280 init="new PrimitiveLoader<Float>(Float.class)", | |
281 ), | |
282 u"http://www.w3.org/2001/XMLSchema#double": TypeDef( | |
283 instance_type="Double", | |
284 name="DoubleInstance", | |
285 loader_type="Loader<Double>", | |
286 init="new PrimitiveLoader<Double>(Double.class)", | |
287 ), | |
288 u"http://www.w3.org/2001/XMLSchema#boolean": TypeDef( | |
289 instance_type="Boolean", | |
290 name="BooleanInstance", | |
291 loader_type="Loader<Boolean>", | |
292 init="new PrimitiveLoader<Boolean>(Boolean.class)", | |
293 ), | |
294 u"https://w3id.org/cwl/salad#null": TypeDef( | |
295 instance_type="Object", | |
296 name="NullInstance", | |
297 loader_type="Loader<Object>", | |
298 init="new NullLoader()", | |
299 ), | |
300 u"https://w3id.org/cwl/salad#Any": TypeDef( | |
301 instance_type="Object", | |
302 name="AnyInstance", | |
303 init="new AnyLoader()", | |
304 loader_type="Loader<Object>", | |
305 ), | |
306 } | |
307 | |
308 def type_loader(self, type_declaration): | |
309 # type: (Union[List[Any], Dict[Text, Any], Text]) -> TypeDef | |
310 if isinstance(type_declaration, MutableSequence): | |
311 sub = [self.type_loader(i) for i in type_declaration] | |
312 if len(sub) < 2: | |
313 return sub[0] | |
314 | |
315 if len(sub) == 2: | |
316 type_1 = sub[0] | |
317 type_2 = sub[1] | |
318 type_1_name = type_1.name | |
319 type_2_name = type_2.name | |
320 if type_1_name == "NullInstance" or type_2_name == "NullInstance": | |
321 non_null_type = type_1 if type_1.name != "NullInstance" else type_2 | |
322 return self.declare_type( | |
323 TypeDef( | |
324 instance_type="java.util.Optional<{}>".format( | |
325 non_null_type.instance_type | |
326 ), | |
327 init="new OptionalLoader({})".format(non_null_type.name), | |
328 name="optional_{}".format(non_null_type.name), | |
329 loader_type="Loader<java.util.Optional<{}>>".format( | |
330 non_null_type.instance_type | |
331 ), | |
332 ) | |
333 ) | |
334 if ( | |
335 type_1_name == "array_of_{}".format(type_2_name) | |
336 or type_2_name == "array_of_{}".format(type_1_name) | |
337 ) and USE_ONE_OR_LIST_OF_TYPES: | |
338 if type_1_name == "array_of_{}".format(type_2_name): | |
339 single_type = type_2 | |
340 array_type = type_1 | |
341 else: | |
342 single_type = type_1 | |
343 array_type = type_2 | |
344 fqclass = "{}.{}".format(self.package, single_type.instance_type) | |
345 return self.declare_type( | |
346 TypeDef( | |
347 instance_type="{}.utils.OneOrListOf<{}>".format( | |
348 self.package, fqclass | |
349 ), | |
350 init="new OneOrListOfLoader<{}>({}, {})".format( | |
351 fqclass, single_type.name, array_type.name | |
352 ), | |
353 name="one_or_array_of_{}".format(single_type.name), | |
354 loader_type="Loader<{}.utils.OneOrListOf<{}>>".format( | |
355 self.package, fqclass | |
356 ), | |
357 ) | |
358 ) | |
359 return self.declare_type( | |
360 TypeDef( | |
361 instance_type="Object", | |
362 init="new UnionLoader(new Loader[] {{ {} }})".format( | |
363 ", ".join(s.name for s in sub) | |
364 ), | |
365 name="union_of_{}".format("_or_".join(s.name for s in sub)), | |
366 loader_type="Loader<Object>", | |
367 ) | |
368 ) | |
369 if isinstance(type_declaration, MutableMapping): | |
370 if type_declaration["type"] in ( | |
371 "array", | |
372 "https://w3id.org/cwl/salad#array", | |
373 ): | |
374 i = self.type_loader(type_declaration["items"]) | |
375 return self.declare_type( | |
376 TypeDef( | |
377 # special doesn't work out with subclassing, gotta be more clever | |
378 # instance_type="List<{}>".format(i.instance_type), | |
379 instance_type="java.util.List<Object>", | |
380 name="array_of_{}".format(i.name), | |
381 init="new ArrayLoader({})".format(i.name), | |
382 loader_type="Loader<java.util.List<{}>>".format( | |
383 i.instance_type | |
384 ), | |
385 ) | |
386 ) | |
387 if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): | |
388 return self.type_loader_enum(type_declaration) | |
389 if type_declaration["type"] in ( | |
390 "record", | |
391 "https://w3id.org/cwl/salad#record", | |
392 ): | |
393 is_abstract = type_declaration.get("abstract", False) | |
394 fqclass = "{}.{}".format( | |
395 self.package, self.safe_name(type_declaration["name"]) | |
396 ) | |
397 return self.declare_type( | |
398 TypeDef( | |
399 instance_type=self.safe_name(type_declaration["name"]), | |
400 name=self.safe_name(type_declaration["name"]), | |
401 init="new RecordLoader<{clazz}>({clazz}{ext}.class)".format( | |
402 clazz=fqclass, ext="Impl" if not is_abstract else "", | |
403 ), | |
404 loader_type="Loader<{}>".format(fqclass), | |
405 ) | |
406 ) | |
407 raise SchemaException("wft {}".format(type_declaration["type"])) | |
408 if type_declaration in self.prims: | |
409 return self.prims[type_declaration] | |
410 return self.collected_types[self.safe_name(type_declaration)] | |
411 | |
412 def type_loader_enum(self, type_declaration): | |
413 # type: (Dict[Text, Any]) -> TypeDef | |
414 symbols = [self.property_name(sym) for sym in type_declaration["symbols"]] | |
415 for sym in symbols: | |
416 self.add_vocab(shortname(sym), sym) | |
417 clazz = self.safe_name(type_declaration["name"]) | |
418 symbols_decl = 'new String[] {{"{}"}}'.format( | |
419 '", "'.join(sym for sym in symbols) | |
420 ) | |
421 enum_path = os.path.join(self.main_src_dir, "{}.java".format(clazz)) | |
422 with open(enum_path, "w") as f: | |
423 f.write( | |
424 """package {package}; | |
425 | |
426 import {package}.utils.ValidationException; | |
427 | |
428 public enum {clazz} {{ | |
429 """.format( | |
430 package=self.package, clazz=clazz | |
431 ) | |
432 ) | |
433 for i, sym in enumerate(symbols): | |
434 suffix = "," if i < (len(symbols) - 1) else ";" | |
435 const = self.safe_name(sym).replace("-", "_").replace(".", "_").upper() | |
436 f.write( | |
437 """ {const}("{val}"){suffix}\n""".format( | |
438 const=const, val=sym, suffix=suffix | |
439 ) | |
440 ) | |
441 f.write( | |
442 """ | |
443 private static String[] symbols = {symbols_decl}; | |
444 private String docVal; | |
445 | |
446 private {clazz}(final String docVal) {{ | |
447 this.docVal = docVal; | |
448 }} | |
449 | |
450 public static {clazz} fromDocumentVal(final String docVal) {{ | |
451 for(final {clazz} val : {clazz}.values()) {{ | |
452 if(val.docVal.equals(docVal)) {{ | |
453 return val; | |
454 }} | |
455 }} | |
456 throw new ValidationException(String.format("Expected one of %s", {clazz}.symbols, docVal)); | |
457 }} | |
458 }} | |
459 """.format( | |
460 clazz=clazz, symbols_decl=symbols_decl | |
461 ) | |
462 ) | |
463 return self.declare_type( | |
464 TypeDef( | |
465 instance_type=clazz, | |
466 name=self.safe_name(type_declaration["name"]), | |
467 loader_type="Loader<{clazz}>".format(clazz=clazz), | |
468 init="new EnumLoader({clazz}.class)".format(clazz=clazz), | |
469 ) | |
470 ) | |
471 | |
472 def declare_field(self, name, fieldtype, doc, optional): | |
473 # type: (Text, TypeDef, Text, bool) -> None | |
474 fieldname = name | |
475 property_name = self.property_name(fieldname) | |
476 cap_case_property_name = property_name[0].upper() + property_name[1:] | |
477 if cap_case_property_name == "Class": | |
478 cap_case_property_name = "Class_" | |
479 | |
480 safename = self.safe_name(fieldname) | |
481 self.current_fieldtypes[property_name] = fieldtype | |
482 getter_doc_str = """ /** | |
483 * Getter for property <I>{fieldname}</I><BR> | |
484 {field_doc_str} | |
485 */ | |
486 """.format( | |
487 fieldname=fieldname, field_doc_str=doc_to_doc_string(doc, indent_level=1) | |
488 ) | |
489 with open( | |
490 os.path.join(self.main_src_dir, "{}.java".format(self.current_class)), "a" | |
491 ) as f: | |
492 f.write( | |
493 """ | |
494 {getter_doc_str} | |
495 {type} get{capfieldname}();""".format( | |
496 getter_doc_str=getter_doc_str, | |
497 capfieldname=cap_case_property_name, | |
498 type=fieldtype.instance_type, | |
499 ) | |
500 ) | |
501 | |
502 if self.current_class_is_abstract: | |
503 return | |
504 | |
505 self.current_fields.write( | |
506 """ | |
507 private {type} {safename}; | |
508 | |
509 {getter_doc_str} | |
510 public {type} get{capfieldname}() {{ | |
511 return this.{safename}; | |
512 }} | |
513 """.format( | |
514 safename=safename, | |
515 capfieldname=cap_case_property_name, | |
516 getter_doc_str=getter_doc_str, | |
517 type=fieldtype.instance_type, | |
518 ) | |
519 ) | |
520 | |
521 self.current_loader.write( | |
522 """ {type} {safename}; | |
523 """.format( | |
524 type=fieldtype.instance_type, safename=safename | |
525 ) | |
526 ) | |
527 if optional: | |
528 self.current_loader.write( | |
529 """ | |
530 if (__doc.containsKey("{fieldname}")) {{ | |
531 """.format( | |
532 fieldname=property_name | |
533 ) | |
534 ) | |
535 spc = " " | |
536 else: | |
537 spc = "" | |
538 | |
539 self.current_loader.write( | |
540 """{spc} try {{ | |
541 {spc} {safename} = | |
542 {spc} LoaderInstances | |
543 {spc} .{fieldtype} | |
544 {spc} .loadField(__doc.get("{fieldname}"), __baseUri, __loadingOptions); | |
545 {spc} }} catch (ValidationException e) {{ | |
546 {spc} {safename} = null; // won't be used but prevents compiler from complaining. | |
547 {spc} final String __message = "the `{fieldname}` field is not valid because:"; | |
548 {spc} __errors.add(new ValidationException(__message, e)); | |
549 {spc} }} | |
550 """.format( | |
551 fieldtype=fieldtype.name, | |
552 safename=safename, | |
553 fieldname=property_name, | |
554 spc=spc, | |
555 ) | |
556 ) | |
557 | |
558 if optional: | |
559 self.current_loader.write( | |
560 """ | |
561 }} else {{ | |
562 {safename} = null; | |
563 }} | |
564 """.format( | |
565 safename=safename | |
566 ) | |
567 ) | |
568 | |
569 def declare_id_field(self, name, fieldtype, doc, optional): | |
570 # type: (Text, TypeDef, Text, bool) -> None | |
571 if self.current_class_is_abstract: | |
572 return | |
573 | |
574 self.declare_field(name, fieldtype, doc, True) | |
575 if optional: | |
576 set_uri = """ | |
577 if ({safename} == null) {{ | |
578 if (__docRoot != null) {{ | |
579 {safename} = java.util.Optional.of(__docRoot); | |
580 }} else {{ | |
581 {safename} = java.util.Optional.of("_:" + java.util.UUID.randomUUID().toString()); | |
582 }} | |
583 }} | |
584 __baseUri = (String) {safename}.orElse(null); | |
585 """ | |
586 else: | |
587 set_uri = """ | |
588 if ({safename} == null) {{ | |
589 if (__docRoot != null) {{ | |
590 {safename} = __docRoot; | |
591 }} else {{ | |
592 throw new ValidationException("Missing {fieldname}"); | |
593 }} | |
594 }} | |
595 __baseUri = (String) {safename}; | |
596 """ | |
597 self.current_loader.write( | |
598 set_uri.format(safename=self.safe_name(name), fieldname=shortname(name)) | |
599 ) | |
600 | |
601 def uri_loader(self, inner, scoped_id, vocab_term, ref_scope): | |
602 # type: (TypeDef, bool, bool, Union[int, None]) -> TypeDef | |
603 assert inner is not None | |
604 instance_type = inner.instance_type or "Object" | |
605 return self.declare_type( | |
606 TypeDef( | |
607 instance_type=instance_type, # ? | |
608 name="uri_{}_{}_{}_{}".format( | |
609 inner.name, scoped_id, vocab_term, ref_scope | |
610 ), | |
611 init="new UriLoader({}, {}, {}, {})".format( | |
612 inner.name, | |
613 self.to_java(scoped_id), | |
614 self.to_java(vocab_term), | |
615 self.to_java(ref_scope), | |
616 ), | |
617 is_uri=True, | |
618 scoped_id=scoped_id, | |
619 ref_scope=ref_scope, | |
620 loader_type="Loader<{}>".format(instance_type), | |
621 ) | |
622 ) | |
623 | |
624 def idmap_loader(self, field, inner, map_subject, map_predicate): | |
625 # type: (Text, TypeDef, Text, Union[Text, None]) -> TypeDef | |
626 assert inner is not None | |
627 instance_type = inner.instance_type or "Object" | |
628 return self.declare_type( | |
629 TypeDef( | |
630 instance_type=instance_type, | |
631 name="idmap_{}_{}".format(self.safe_name(field), inner.name), | |
632 init='new IdMapLoader({}, "{}", "{}")'.format( | |
633 inner.name, map_subject, map_predicate | |
634 ), | |
635 loader_type="Loader<{}>".format(instance_type), | |
636 ) | |
637 ) | |
638 | |
639 def typedsl_loader(self, inner, ref_scope): | |
640 # type: (TypeDef, Union[int, None]) -> TypeDef | |
641 assert inner is not None | |
642 instance_type = inner.instance_type or "Object" | |
643 return self.declare_type( | |
644 TypeDef( | |
645 instance_type=instance_type, | |
646 name="typedsl_{}_{}".format(inner.name, ref_scope), | |
647 init="new TypeDslLoader({}, {})".format(inner.name, ref_scope), | |
648 loader_type="Loader<{}>".format(instance_type), | |
649 ) | |
650 ) | |
651 | |
652 return inner | |
653 | |
654 def to_java(self, val): # type: (Any) -> Any | |
655 if val is True: | |
656 return "true" | |
657 elif val is None: | |
658 return "null" | |
659 elif val is False: | |
660 return "false" | |
661 return val | |
662 | |
663 def epilogue(self, root_loader): # type: (TypeDef) -> None | |
664 pd = u"This project contains Java objects and utilities " | |
665 pd = pd + ' auto-generated by <a href="https://github.com/' | |
666 pd = pd + 'common-workflow-language/schema_salad">Schema Salad</a>' | |
667 pd = pd + " for parsing documents corresponding to the " | |
668 pd = pd + str(self.base_uri) + " schema." | |
669 | |
670 template_vars = dict( | |
671 base_uri=self.base_uri, | |
672 package=self.package, | |
673 group_id=self.package, | |
674 artifact_id=self.artifact, | |
675 version="0.0.1-SNAPSHOT", | |
676 project_name=self.package, | |
677 project_description=pd, | |
678 license_name="Apache License, Version 2.0", | |
679 license_url="https://www.apache.org/licenses/LICENSE-2.0.txt", | |
680 ) # type: MutableMapping[Text, Text] | |
681 | |
682 def template_from_resource(resource): | |
683 # type: (str) -> string.Template | |
684 template_str = pkg_resources.resource_string( | |
685 __name__, "java/%s" % resource | |
686 ).decode("utf-8") | |
687 template = string.Template(template_str) | |
688 return template | |
689 | |
690 def expand_resource_template_to(resource, path): | |
691 # type: (str, str) -> None | |
692 template = template_from_resource(resource) | |
693 src = template.safe_substitute(template_vars) | |
694 _ensure_directory_and_write(path, src) | |
695 | |
696 expand_resource_template_to("pom.xml", os.path.join(self.target_dir, "pom.xml")) | |
697 expand_resource_template_to( | |
698 "gitignore", os.path.join(self.target_dir, ".gitignore") | |
699 ) | |
700 expand_resource_template_to( | |
701 "package.html", os.path.join(self.main_src_dir, "package.html") | |
702 ) | |
703 expand_resource_template_to( | |
704 "overview.html", | |
705 os.path.join(self.target_dir, "src", "main", "javadoc", "overview.html"), | |
706 ) | |
707 expand_resource_template_to( | |
708 "MANIFEST.MF", | |
709 os.path.join( | |
710 self.target_dir, "src", "main", "resources", "META-INF", "MANIFEST.MF" | |
711 ), | |
712 ) | |
713 expand_resource_template_to( | |
714 "README.md", os.path.join(self.target_dir, "README.md"), | |
715 ) | |
716 | |
717 vocab = "" | |
718 rvocab = "" | |
719 for k in sorted(self.vocab.keys()): | |
720 vocab += """ vocab.put("{}", "{}");\n""".format(k, self.vocab[k]) | |
721 rvocab += """ rvocab.put("{}", "{}");\n""".format(self.vocab[k], k) | |
722 | |
723 loader_instances = "" | |
724 for _, collected_type in iteritems(self.collected_types): | |
725 loader_instances += " public static {} {} = {};\n".format( | |
726 collected_type.loader_type, collected_type.name, collected_type.init | |
727 ) | |
728 | |
729 example_tests = "" | |
730 if self.examples: | |
731 _safe_makedirs(self.test_resources_dir) | |
732 utils_resources = os.path.join(self.test_resources_dir, "utils") | |
733 if os.path.exists(utils_resources): | |
734 shutil.rmtree(utils_resources) | |
735 shutil.copytree(self.examples, utils_resources) | |
736 for example_name in os.listdir(self.examples): | |
737 if example_name.startswith("valid"): | |
738 basename = os.path.basename(example_name).split(".", 1)[0] | |
739 example_tests += """ | |
740 @org.junit.Test | |
741 public void test{basename}ByString() throws Exception {{ | |
742 String path = java.nio.file.Paths.get(".").toAbsolutePath().normalize().toString(); | |
743 String baseUri = Uris.fileUri(path) + "/"; | |
744 java.net.URL url = getClass().getResource("{example_name}"); | |
745 java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); | |
746 String yaml = new String(java.nio.file.Files.readAllBytes(resPath), "UTF8"); | |
747 RootLoader.loadDocument(yaml, baseUri); | |
748 }} | |
749 | |
750 @org.junit.Test | |
751 public void test{basename}ByPath() throws Exception {{ | |
752 java.net.URL url = getClass().getResource("{example_name}"); | |
753 java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); | |
754 RootLoader.loadDocument(resPath); | |
755 }} | |
756 | |
757 @org.junit.Test | |
758 public void test{basename}ByMap() throws Exception {{ | |
759 java.net.URL url = getClass().getResource("{example_name}"); | |
760 java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); | |
761 String yaml = new String(java.nio.file.Files.readAllBytes(resPath), "UTF8"); | |
762 java.util.Map<String, Object> doc; | |
763 doc = (java.util.Map<String, Object>) YamlUtils.mapFromString(yaml); | |
764 RootLoader.loadDocument(doc); | |
765 }}""".format( | |
766 basename=basename, example_name=example_name, | |
767 ) | |
768 | |
769 template_args = dict( | |
770 package=self.package, | |
771 vocab=vocab, | |
772 rvocab=rvocab, | |
773 loader_instances=loader_instances, | |
774 root_loader_name=root_loader.name, | |
775 root_loader_instance_type=root_loader.instance_type or "Object", | |
776 example_tests=example_tests, | |
777 ) # type: MutableMapping[Text, Text] | |
778 | |
779 util_src_dirs = { | |
780 "main_utils": self.main_src_dir, | |
781 "test_utils": self.test_src_dir, | |
782 } | |
783 for (util_src, util_target) in util_src_dirs.items(): | |
784 for util in pkg_resources.resource_listdir(__name__, "java/%s" % util_src): | |
785 src_path = os.path.join(util_target, "utils", util) | |
786 src_template = template_from_resource(os.path.join(util_src, util)) | |
787 src = src_template.safe_substitute(template_args) | |
788 _ensure_directory_and_write(src_path, src) |