comparison env/lib/python3.7/site-packages/schema_salad/java_codegen.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
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)