Mercurial > repos > shellac > guppy_basecaller
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/lib/python3.7/site-packages/schema_salad/java_codegen.py Thu May 14 14:56:58 2020 -0400 @@ -0,0 +1,788 @@ +"""Work-in-progress Java code generator for a given schema salad definition.""" +import os +import pkg_resources +import string +import shutil +from io import open as io_open +from typing import Any, Dict, List, MutableMapping, MutableSequence, Optional, Union + +from six import iteritems, itervalues +from six.moves import cStringIO, urllib +from typing_extensions import Text # pylint: disable=unused-import + +from . import schema +from .codegen_base import CodeGenBase, TypeDef +from .exceptions import SchemaException +from .schema import shortname + +# move to a regular typing import when Python 3.3-3.6 is no longer supported + +# experiment at providing more typed objects building a optional type that allows +# referencing one or a list of objects. It is useful for improving the RootLoader +# for simple schema with a single root loader - but doesn't help with CWL at all and +# may even confuse things a bit so turning these off be default. +USE_ONE_OR_LIST_OF_TYPES = False + + +def _ensure_directory_and_write(path, contents): + # type: (Text, Text) -> None + dirname = os.path.dirname(path) + _safe_makedirs(dirname) + with io_open(path, mode="w", encoding="utf-8") as f: + f.write(contents) + + +def doc_to_doc_string(doc, indent_level=0): + # type: (Text, int) -> Text + lead = " " + " " * indent_level + "* " * indent_level + if doc: + doc_str = "{}<BLOCKQUOTE>\n".format(lead) + doc_str += "\n".join(["{}{}".format(lead, l) for l in doc.split("\n")]) + doc_str += "{}</BLOCKQUOTE>".format(lead) + else: + doc_str = "" + return doc_str + + +def _safe_makedirs(path): + # type: (Text) -> None + if not os.path.exists(path): + os.makedirs(path) + + +class JavaCodeGen(CodeGenBase): + def __init__(self, base, target, examples): + # type: (Text, Optional[str], Optional[str]) -> None + super(JavaCodeGen, self).__init__() + self.base_uri = base + sp = urllib.parse.urlsplit(base) + self.examples = examples + self.package = ".".join( + list(reversed(sp.netloc.split("."))) + sp.path.strip("/").split("/") + ) + self.artifact = self.package.split(".")[-1] + target = target or "." + self.target_dir = target + rel_package_dir = self.package.replace(".", "/") + self.rel_package_dir = rel_package_dir + self.main_src_dir = os.path.join( + self.target_dir, "src", "main", "java", rel_package_dir + ) + self.test_src_dir = os.path.join( + self.target_dir, "src", "test", "java", rel_package_dir + ) + self.test_resources_dir = os.path.join( + self.target_dir, "src", "test", "resources", rel_package_dir + ) + + def prologue(self): # type: () -> None + for src_dir in [self.main_src_dir, self.test_src_dir]: + _safe_makedirs(src_dir) + + for primitive in itervalues(self.prims): + self.declare_type(primitive) + + @staticmethod + def property_name(name): # type: (Text) -> Text + avn = schema.avro_name(name) + return avn + + @staticmethod + def safe_name(name): # type: (Text) -> Text + avn = JavaCodeGen.property_name(name) + if avn in ("class", "extends", "abstract", "default", "package"): + # reserved words + avn = avn + "_" + return avn + + def interface_name(self, n): + # type: (Text) -> Text + return self.safe_name(n) + + def begin_class( + self, + classname, # type: Text + extends, # type: MutableSequence[Text] + doc, # type: Text + abstract, # type: bool + field_names, # type: MutableSequence[Text] + idfield, # type: Text + ): # type: (...) -> None + cls = self.interface_name(classname) + self.current_class = cls + self.current_class_is_abstract = abstract + self.current_loader = cStringIO() + self.current_fieldtypes = {} # type: Dict[Text, TypeDef] + self.current_fields = cStringIO() + interface_doc_str = u"* Auto-generated interface for <I>%s</I><BR>" % classname + if not abstract: + implemented_by = u"This interface is implemented by {{@link {}Impl}}<BR>" + interface_doc_str += implemented_by.format(cls) + interface_doc_str += doc_to_doc_string(doc) + class_doc_str = u"* Auto-generated class implementation for <I>{}</I><BR>".format( + classname + ) + class_doc_str += doc_to_doc_string(doc) + with open(os.path.join(self.main_src_dir, "{}.java".format(cls)), "w") as f: + + if extends: + ext = ( + "extends " + + ", ".join(self.interface_name(e) for e in extends) + + ", Savable" + ) + else: + ext = "extends Savable" + f.write( + """package {package}; + +import {package}.utils.Savable; + +/** +{interface_doc_str} + */ +public interface {cls} {ext} {{""".format( + package=self.package, + cls=cls, + ext=ext, + interface_doc_str=interface_doc_str, + ) + ) + + if self.current_class_is_abstract: + return + + with open(os.path.join(self.main_src_dir, "{}Impl.java".format(cls)), "w") as f: + f.write( + """package {package}; + +import {package}.utils.LoaderInstances; +import {package}.utils.LoadingOptions; +import {package}.utils.LoadingOptionsBuilder; +import {package}.utils.SavableImpl; +import {package}.utils.ValidationException; + +/** +{class_doc_str} + */ +public class {cls}Impl extends SavableImpl implements {cls} {{ + private LoadingOptions loadingOptions_ = new LoadingOptionsBuilder().build(); + private java.util.Map<String, Object> extensionFields_ = + new java.util.HashMap<String, Object>(); +""".format( + package=self.package, cls=cls, class_doc_str=class_doc_str + ) + ) + self.current_loader.write( + """ + /** + * Used by {{@link {package}.utils.RootLoader}} to construct instances of {cls}Impl. + * + * @param __doc_ Document fragment to load this record object from (presumably a + {{@link java.util.Map}}). + * @param __baseUri_ Base URI to generate child document IDs against. + * @param __loadingOptions Context for loading URIs and populating objects. + * @param __docRoot_ ID at this position in the document (if available) (maybe?) + * @throws ValidationException If the document fragment is not a {{@link java.util.Map}} + * or validation of fields fails. + */ + public {cls}Impl( + final Object __doc_, + final String __baseUri_, + LoadingOptions __loadingOptions, + final String __docRoot_) {{ + super(__doc_, __baseUri_, __loadingOptions, __docRoot_); + // Prefix plumbing variables with '__' to reduce likelihood of collision with + // generated names. + String __baseUri = __baseUri_; + String __docRoot = __docRoot_; + if (!(__doc_ instanceof java.util.Map)) {{ + throw new ValidationException("{cls}Impl called on non-map"); + }} + final java.util.Map<String, Object> __doc = (java.util.Map<String, Object>) __doc_; + final java.util.List<ValidationException> __errors = + new java.util.ArrayList<ValidationException>(); + if (__loadingOptions != null) {{ + this.loadingOptions_ = __loadingOptions; + }} +""".format( + cls=cls, package=self.package + ) + ) + + def end_class(self, classname, field_names): + # type: (Text, List[Text]) -> None + with open( + os.path.join(self.main_src_dir, "{}.java".format(self.current_class)), "a" + ) as f: + f.write( + """ +} +""" + ) + if self.current_class_is_abstract: + return + + self.current_loader.write( + """ if (!__errors.isEmpty()) { + throw new ValidationException("Trying 'RecordField'", __errors); + } +""" + ) + for fieldname in field_names: + fieldtype = self.current_fieldtypes.get(fieldname) + if fieldtype is None: + continue + self.current_loader.write( + """ this.{safename} = ({type}) {safename}; +""".format( + safename=self.safe_name(fieldname), type=fieldtype.instance_type + ) + ) + + self.current_loader.write(""" }""") + + with open( + os.path.join(self.main_src_dir, "{}Impl.java".format(self.current_class)), + "a", + ) as f: + f.write(self.current_fields.getvalue()) + f.write(self.current_loader.getvalue()) + f.write( + """ +} +""" + ) + + prims = { + u"http://www.w3.org/2001/XMLSchema#string": TypeDef( + instance_type="String", + init="new PrimitiveLoader<String>(String.class)", + name="StringInstance", + loader_type="Loader<String>", + ), + u"http://www.w3.org/2001/XMLSchema#int": TypeDef( + instance_type="Integer", + init="new PrimitiveLoader<Integer>(Integer.class)", + name="IntegerInstance", + loader_type="Loader<Integer>", + ), + u"http://www.w3.org/2001/XMLSchema#long": TypeDef( + instance_type="Long", + name="LongInstance", + loader_type="Loader<Long>", + init="new PrimitiveLoader<Long>(Long.class)", + ), + u"http://www.w3.org/2001/XMLSchema#float": TypeDef( + instance_type="Float", + name="FloatInstance", + loader_type="Loader<Float>", + init="new PrimitiveLoader<Float>(Float.class)", + ), + u"http://www.w3.org/2001/XMLSchema#double": TypeDef( + instance_type="Double", + name="DoubleInstance", + loader_type="Loader<Double>", + init="new PrimitiveLoader<Double>(Double.class)", + ), + u"http://www.w3.org/2001/XMLSchema#boolean": TypeDef( + instance_type="Boolean", + name="BooleanInstance", + loader_type="Loader<Boolean>", + init="new PrimitiveLoader<Boolean>(Boolean.class)", + ), + u"https://w3id.org/cwl/salad#null": TypeDef( + instance_type="Object", + name="NullInstance", + loader_type="Loader<Object>", + init="new NullLoader()", + ), + u"https://w3id.org/cwl/salad#Any": TypeDef( + instance_type="Object", + name="AnyInstance", + init="new AnyLoader()", + loader_type="Loader<Object>", + ), + } + + def type_loader(self, type_declaration): + # type: (Union[List[Any], Dict[Text, Any], Text]) -> TypeDef + if isinstance(type_declaration, MutableSequence): + sub = [self.type_loader(i) for i in type_declaration] + if len(sub) < 2: + return sub[0] + + if len(sub) == 2: + type_1 = sub[0] + type_2 = sub[1] + type_1_name = type_1.name + type_2_name = type_2.name + if type_1_name == "NullInstance" or type_2_name == "NullInstance": + non_null_type = type_1 if type_1.name != "NullInstance" else type_2 + return self.declare_type( + TypeDef( + instance_type="java.util.Optional<{}>".format( + non_null_type.instance_type + ), + init="new OptionalLoader({})".format(non_null_type.name), + name="optional_{}".format(non_null_type.name), + loader_type="Loader<java.util.Optional<{}>>".format( + non_null_type.instance_type + ), + ) + ) + if ( + type_1_name == "array_of_{}".format(type_2_name) + or type_2_name == "array_of_{}".format(type_1_name) + ) and USE_ONE_OR_LIST_OF_TYPES: + if type_1_name == "array_of_{}".format(type_2_name): + single_type = type_2 + array_type = type_1 + else: + single_type = type_1 + array_type = type_2 + fqclass = "{}.{}".format(self.package, single_type.instance_type) + return self.declare_type( + TypeDef( + instance_type="{}.utils.OneOrListOf<{}>".format( + self.package, fqclass + ), + init="new OneOrListOfLoader<{}>({}, {})".format( + fqclass, single_type.name, array_type.name + ), + name="one_or_array_of_{}".format(single_type.name), + loader_type="Loader<{}.utils.OneOrListOf<{}>>".format( + self.package, fqclass + ), + ) + ) + return self.declare_type( + TypeDef( + instance_type="Object", + init="new UnionLoader(new Loader[] {{ {} }})".format( + ", ".join(s.name for s in sub) + ), + name="union_of_{}".format("_or_".join(s.name for s in sub)), + loader_type="Loader<Object>", + ) + ) + if isinstance(type_declaration, MutableMapping): + if type_declaration["type"] in ( + "array", + "https://w3id.org/cwl/salad#array", + ): + i = self.type_loader(type_declaration["items"]) + return self.declare_type( + TypeDef( + # special doesn't work out with subclassing, gotta be more clever + # instance_type="List<{}>".format(i.instance_type), + instance_type="java.util.List<Object>", + name="array_of_{}".format(i.name), + init="new ArrayLoader({})".format(i.name), + loader_type="Loader<java.util.List<{}>>".format( + i.instance_type + ), + ) + ) + if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): + return self.type_loader_enum(type_declaration) + if type_declaration["type"] in ( + "record", + "https://w3id.org/cwl/salad#record", + ): + is_abstract = type_declaration.get("abstract", False) + fqclass = "{}.{}".format( + self.package, self.safe_name(type_declaration["name"]) + ) + return self.declare_type( + TypeDef( + instance_type=self.safe_name(type_declaration["name"]), + name=self.safe_name(type_declaration["name"]), + init="new RecordLoader<{clazz}>({clazz}{ext}.class)".format( + clazz=fqclass, ext="Impl" if not is_abstract else "", + ), + loader_type="Loader<{}>".format(fqclass), + ) + ) + raise SchemaException("wft {}".format(type_declaration["type"])) + if type_declaration in self.prims: + return self.prims[type_declaration] + return self.collected_types[self.safe_name(type_declaration)] + + def type_loader_enum(self, type_declaration): + # type: (Dict[Text, Any]) -> TypeDef + symbols = [self.property_name(sym) for sym in type_declaration["symbols"]] + for sym in symbols: + self.add_vocab(shortname(sym), sym) + clazz = self.safe_name(type_declaration["name"]) + symbols_decl = 'new String[] {{"{}"}}'.format( + '", "'.join(sym for sym in symbols) + ) + enum_path = os.path.join(self.main_src_dir, "{}.java".format(clazz)) + with open(enum_path, "w") as f: + f.write( + """package {package}; + +import {package}.utils.ValidationException; + +public enum {clazz} {{ +""".format( + package=self.package, clazz=clazz + ) + ) + for i, sym in enumerate(symbols): + suffix = "," if i < (len(symbols) - 1) else ";" + const = self.safe_name(sym).replace("-", "_").replace(".", "_").upper() + f.write( + """ {const}("{val}"){suffix}\n""".format( + const=const, val=sym, suffix=suffix + ) + ) + f.write( + """ + private static String[] symbols = {symbols_decl}; + private String docVal; + + private {clazz}(final String docVal) {{ + this.docVal = docVal; + }} + + public static {clazz} fromDocumentVal(final String docVal) {{ + for(final {clazz} val : {clazz}.values()) {{ + if(val.docVal.equals(docVal)) {{ + return val; + }} + }} + throw new ValidationException(String.format("Expected one of %s", {clazz}.symbols, docVal)); + }} +}} +""".format( + clazz=clazz, symbols_decl=symbols_decl + ) + ) + return self.declare_type( + TypeDef( + instance_type=clazz, + name=self.safe_name(type_declaration["name"]), + loader_type="Loader<{clazz}>".format(clazz=clazz), + init="new EnumLoader({clazz}.class)".format(clazz=clazz), + ) + ) + + def declare_field(self, name, fieldtype, doc, optional): + # type: (Text, TypeDef, Text, bool) -> None + fieldname = name + property_name = self.property_name(fieldname) + cap_case_property_name = property_name[0].upper() + property_name[1:] + if cap_case_property_name == "Class": + cap_case_property_name = "Class_" + + safename = self.safe_name(fieldname) + self.current_fieldtypes[property_name] = fieldtype + getter_doc_str = """ /** + * Getter for property <I>{fieldname}</I><BR> +{field_doc_str} + */ +""".format( + fieldname=fieldname, field_doc_str=doc_to_doc_string(doc, indent_level=1) + ) + with open( + os.path.join(self.main_src_dir, "{}.java".format(self.current_class)), "a" + ) as f: + f.write( + """ +{getter_doc_str} + {type} get{capfieldname}();""".format( + getter_doc_str=getter_doc_str, + capfieldname=cap_case_property_name, + type=fieldtype.instance_type, + ) + ) + + if self.current_class_is_abstract: + return + + self.current_fields.write( + """ + private {type} {safename}; + +{getter_doc_str} + public {type} get{capfieldname}() {{ + return this.{safename}; + }} +""".format( + safename=safename, + capfieldname=cap_case_property_name, + getter_doc_str=getter_doc_str, + type=fieldtype.instance_type, + ) + ) + + self.current_loader.write( + """ {type} {safename}; +""".format( + type=fieldtype.instance_type, safename=safename + ) + ) + if optional: + self.current_loader.write( + """ + if (__doc.containsKey("{fieldname}")) {{ +""".format( + fieldname=property_name + ) + ) + spc = " " + else: + spc = "" + + self.current_loader.write( + """{spc} try {{ +{spc} {safename} = +{spc} LoaderInstances +{spc} .{fieldtype} +{spc} .loadField(__doc.get("{fieldname}"), __baseUri, __loadingOptions); +{spc} }} catch (ValidationException e) {{ +{spc} {safename} = null; // won't be used but prevents compiler from complaining. +{spc} final String __message = "the `{fieldname}` field is not valid because:"; +{spc} __errors.add(new ValidationException(__message, e)); +{spc} }} +""".format( + fieldtype=fieldtype.name, + safename=safename, + fieldname=property_name, + spc=spc, + ) + ) + + if optional: + self.current_loader.write( + """ + }} else {{ + {safename} = null; + }} +""".format( + safename=safename + ) + ) + + def declare_id_field(self, name, fieldtype, doc, optional): + # type: (Text, TypeDef, Text, bool) -> None + if self.current_class_is_abstract: + return + + self.declare_field(name, fieldtype, doc, True) + if optional: + set_uri = """ + if ({safename} == null) {{ + if (__docRoot != null) {{ + {safename} = java.util.Optional.of(__docRoot); + }} else {{ + {safename} = java.util.Optional.of("_:" + java.util.UUID.randomUUID().toString()); + }} + }} + __baseUri = (String) {safename}.orElse(null); +""" + else: + set_uri = """ + if ({safename} == null) {{ + if (__docRoot != null) {{ + {safename} = __docRoot; + }} else {{ + throw new ValidationException("Missing {fieldname}"); + }} + }} + __baseUri = (String) {safename}; +""" + self.current_loader.write( + set_uri.format(safename=self.safe_name(name), fieldname=shortname(name)) + ) + + def uri_loader(self, inner, scoped_id, vocab_term, ref_scope): + # type: (TypeDef, bool, bool, Union[int, None]) -> TypeDef + assert inner is not None + instance_type = inner.instance_type or "Object" + return self.declare_type( + TypeDef( + instance_type=instance_type, # ? + name="uri_{}_{}_{}_{}".format( + inner.name, scoped_id, vocab_term, ref_scope + ), + init="new UriLoader({}, {}, {}, {})".format( + inner.name, + self.to_java(scoped_id), + self.to_java(vocab_term), + self.to_java(ref_scope), + ), + is_uri=True, + scoped_id=scoped_id, + ref_scope=ref_scope, + loader_type="Loader<{}>".format(instance_type), + ) + ) + + def idmap_loader(self, field, inner, map_subject, map_predicate): + # type: (Text, TypeDef, Text, Union[Text, None]) -> TypeDef + assert inner is not None + instance_type = inner.instance_type or "Object" + return self.declare_type( + TypeDef( + instance_type=instance_type, + name="idmap_{}_{}".format(self.safe_name(field), inner.name), + init='new IdMapLoader({}, "{}", "{}")'.format( + inner.name, map_subject, map_predicate + ), + loader_type="Loader<{}>".format(instance_type), + ) + ) + + def typedsl_loader(self, inner, ref_scope): + # type: (TypeDef, Union[int, None]) -> TypeDef + assert inner is not None + instance_type = inner.instance_type or "Object" + return self.declare_type( + TypeDef( + instance_type=instance_type, + name="typedsl_{}_{}".format(inner.name, ref_scope), + init="new TypeDslLoader({}, {})".format(inner.name, ref_scope), + loader_type="Loader<{}>".format(instance_type), + ) + ) + + return inner + + def to_java(self, val): # type: (Any) -> Any + if val is True: + return "true" + elif val is None: + return "null" + elif val is False: + return "false" + return val + + def epilogue(self, root_loader): # type: (TypeDef) -> None + pd = u"This project contains Java objects and utilities " + pd = pd + ' auto-generated by <a href="https://github.com/' + pd = pd + 'common-workflow-language/schema_salad">Schema Salad</a>' + pd = pd + " for parsing documents corresponding to the " + pd = pd + str(self.base_uri) + " schema." + + template_vars = dict( + base_uri=self.base_uri, + package=self.package, + group_id=self.package, + artifact_id=self.artifact, + version="0.0.1-SNAPSHOT", + project_name=self.package, + project_description=pd, + license_name="Apache License, Version 2.0", + license_url="https://www.apache.org/licenses/LICENSE-2.0.txt", + ) # type: MutableMapping[Text, Text] + + def template_from_resource(resource): + # type: (str) -> string.Template + template_str = pkg_resources.resource_string( + __name__, "java/%s" % resource + ).decode("utf-8") + template = string.Template(template_str) + return template + + def expand_resource_template_to(resource, path): + # type: (str, str) -> None + template = template_from_resource(resource) + src = template.safe_substitute(template_vars) + _ensure_directory_and_write(path, src) + + expand_resource_template_to("pom.xml", os.path.join(self.target_dir, "pom.xml")) + expand_resource_template_to( + "gitignore", os.path.join(self.target_dir, ".gitignore") + ) + expand_resource_template_to( + "package.html", os.path.join(self.main_src_dir, "package.html") + ) + expand_resource_template_to( + "overview.html", + os.path.join(self.target_dir, "src", "main", "javadoc", "overview.html"), + ) + expand_resource_template_to( + "MANIFEST.MF", + os.path.join( + self.target_dir, "src", "main", "resources", "META-INF", "MANIFEST.MF" + ), + ) + expand_resource_template_to( + "README.md", os.path.join(self.target_dir, "README.md"), + ) + + vocab = "" + rvocab = "" + for k in sorted(self.vocab.keys()): + vocab += """ vocab.put("{}", "{}");\n""".format(k, self.vocab[k]) + rvocab += """ rvocab.put("{}", "{}");\n""".format(self.vocab[k], k) + + loader_instances = "" + for _, collected_type in iteritems(self.collected_types): + loader_instances += " public static {} {} = {};\n".format( + collected_type.loader_type, collected_type.name, collected_type.init + ) + + example_tests = "" + if self.examples: + _safe_makedirs(self.test_resources_dir) + utils_resources = os.path.join(self.test_resources_dir, "utils") + if os.path.exists(utils_resources): + shutil.rmtree(utils_resources) + shutil.copytree(self.examples, utils_resources) + for example_name in os.listdir(self.examples): + if example_name.startswith("valid"): + basename = os.path.basename(example_name).split(".", 1)[0] + example_tests += """ + @org.junit.Test + public void test{basename}ByString() throws Exception {{ + String path = java.nio.file.Paths.get(".").toAbsolutePath().normalize().toString(); + String baseUri = Uris.fileUri(path) + "/"; + java.net.URL url = getClass().getResource("{example_name}"); + java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); + String yaml = new String(java.nio.file.Files.readAllBytes(resPath), "UTF8"); + RootLoader.loadDocument(yaml, baseUri); + }} + + @org.junit.Test + public void test{basename}ByPath() throws Exception {{ + java.net.URL url = getClass().getResource("{example_name}"); + java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); + RootLoader.loadDocument(resPath); + }} + + @org.junit.Test + public void test{basename}ByMap() throws Exception {{ + java.net.URL url = getClass().getResource("{example_name}"); + java.nio.file.Path resPath = java.nio.file.Paths.get(url.toURI()); + String yaml = new String(java.nio.file.Files.readAllBytes(resPath), "UTF8"); + java.util.Map<String, Object> doc; + doc = (java.util.Map<String, Object>) YamlUtils.mapFromString(yaml); + RootLoader.loadDocument(doc); + }}""".format( + basename=basename, example_name=example_name, + ) + + template_args = dict( + package=self.package, + vocab=vocab, + rvocab=rvocab, + loader_instances=loader_instances, + root_loader_name=root_loader.name, + root_loader_instance_type=root_loader.instance_type or "Object", + example_tests=example_tests, + ) # type: MutableMapping[Text, Text] + + util_src_dirs = { + "main_utils": self.main_src_dir, + "test_utils": self.test_src_dir, + } + for (util_src, util_target) in util_src_dirs.items(): + for util in pkg_resources.resource_listdir(__name__, "java/%s" % util_src): + src_path = os.path.join(util_target, "utils", util) + src_template = template_from_resource(os.path.join(util_src, util)) + src = src_template.safe_substitute(template_args) + _ensure_directory_and_write(src_path, src)