Java

Overview

The Java generator produces java class files from a LinkML model, with optional support for user-supplied jinja2 templates to generate classes with alternate annotations or additional documentation.

Docs

Command Line

gen-java

Generate java classes to represent a LinkML model

gen-java [OPTIONS] YAMLFILE

Options

-V, --version

Show the version and exit.

--use-aliases, --no-use-aliases

Use aliases when available to name fields

--true-enums, --no-true-enums

Treat enums as distinct types rather than strings

--visitor <visitor>

Generate a visitor interface for the specified class

--extra-template <extra_template>

Name of an additional, arbitrary template to use

--generate-records, --no-generate-records

Optional Java 17 record implementation (deprecated, use –template-variant=records instead)

--template-file <template_file>

Optional jinja2 template to use for class generation (takes precedence over –template-dir)

--template-variant <template_variant>

Use the specified template variant

--template-dir <template_dir>

Directory containing the Jinja2 templates to use

--package <package>

Package name where relevant for generated class files

--output-directory <output_directory>

Output directory for individually generated class files

Default:

'output'

-f, --format <format>

Output format

Default:

'java'

Options:

java

--metadata, --no-metadata

Include metadata in output

Default:

True

--useuris, --metauris

Use class and slot URIs over model uris

Default:

True

-im, --importmap <importmap>

Import mapping file

--log_level <log_level>

Logging level

Default:

'WARNING'

Options:

CRITICAL | ERROR | WARNING | INFO | DEBUG

-v, --verbose

Verbosity. Takes precedence over –log_level.

--mergeimports, --no-mergeimports

Merge imports into source file (default=mergeimports)

--stacktrace, --no-stacktrace

Print a stack trace when an error occurs

Default:

False

Arguments

YAMLFILE

Required argument

Code

class linkml.generators.javagen.JavaGenerator(schema: str | ~typing.TextIO | ~linkml_runtime.linkml_model.meta.SchemaDefinition | Generator | ~pathlib.Path, schemaview: ~linkml_runtime.utils.schemaview.SchemaView = None, format: str | None = None, metadata: bool = True, useuris: bool | None = None, log_level: int | None = 30, mergeimports: bool | None = True, source_file_date: str | None = None, source_file_size: int | None = None, logger: ~logging.Logger | None = None, verbose: bool | None = None, output: str | None = None, namespaces: ~linkml_runtime.utils.namespaces.Namespaces | None = None, directory_output: bool = False, base_dir: str = None, metamodel_name_map: dict[str, str] = None, importmap: str | ~collections.abc.Mapping[str, str] | None = None, emit_prefixes: set[str] = <factory>, metamodel: ~linkml.utils.schemaloader.SchemaLoader = None, stacktrace: bool = False, include: str | ~pathlib.Path | ~linkml_runtime.linkml_model.meta.SchemaDefinition | None = None, template_file: str | None = None, true_enums: bool = False, use_aliases: bool = False, package: str = 'example', template_dir: ~pathlib.Path | None = None, template_cache: ~linkml.generators.javagen.TemplateCache = <factory>, gen_classvars: bool = True, gen_slots: bool = True, genmeta: bool = False)[source]

Generates java code from a LinkML schema.

This generators supports an arbitrary number of different styles through the use of “template variants“.

serialize(directory: str | Path, template_variant: str | None = None, extra_templates: list[str] | None = None, visitors: list[str] | None = None, rendered_module: JavaBundle | None = None, **kwargs) None[source]

Generate the Java code and write it to directory, one file per class.

Java requires one public class per file, so there is no meaningful single-string serialization of a schema; callers that want the generated code in memory should use render() and work from the returned JavaBundle instead.

Parameters:
  • directory – The directory where to write the code files.

  • template_variant – The name of the template variant to use, if any. Ignored when rendered_module is provided.

  • extra_templates – A list of additional templates from which to generate additional code files. See render() for details. Ignored when rendered_module is provided.

  • visitors – A list of class names for which to generate a visitor interface. See render() for details. Ignored when rendered_module is provided.

  • rendered_module – Optional pre-computed JavaBundle to write instead of calling render() afresh. Allows caller to render once and inspect/write multiple times. When supplied, template_variant, extra_templates, and visitors are ignored (the bundle is used as-is).

Configurable Behaviors

Rendering of Enumerations

LinkML enumerations can be rendered in two ways:

  • as plain String objects: that is, the enumeration themselves are not rendered at all, and slots whose range is set to an enumeration are rendered as String-typed fields;

  • as standard Java enum objects.

For backwards compatibility reasons, the default behavior is to render enumerations as String objects. Use the --true-enums option (from the command line) or the true_enums named parameter (in the JavaGenerator constructor) to render LinkML enumerations as standard Java enum objects. Of note, this settings applies to all enumerations defined in the LinkML schema – it is not possible to render some enumerations as String objects and others as standard enum objects.

Use of Slot Aliases

Slots in a LinkML schema can optionally have an alias, which, if present, is intended to be “used instead of the actual slot name”.

By default, the Java generator always derives the name of a field in a class from the actual name of the slot, not from its alias. Use the --use-aliases option (from the command line) or the use_aliases named parameter (in the JavaGenerator constructor) to force the generator to honor the presence of a slot alias.

For example, given the definition of the slot_definitions slot in LinkML’s own metamodel:

slot_definitions:
  domain: schema_definition
  multivalued: true
  range: slot_definition
  inlined: true
  alias: slots

the generator will, by default, render this slot as a field named slotDefinitions (derived from the actual slot name, ignoring the slots alias):

private List<SlotDefinition> slotDefinitions;

With --use-aliases, that slot will instead be rendered as:

private List<SlotDefinition> slots;

Of note, when using the org.incenp.linkml template variant, the slot alias, when present, is always used to determine how the slot is expected to be serialised in the JSON or YAML serialisations; the --use-aliases option only affects the symbol used to represent the slot in the Java code.

Generating Visitor Patterns

The Java generator includes a built-in feature to easily implement a visitor pattern over a class hierarchy defined in a LinkML schema.

Assuming the following schema (simplified excerpt from the KGCL Schema):

classes:
  Change:
    description: Any change perform on an ontology or knowledge graph.
    slots:
      - id
      - type

  SimpleChange:
    is_a: Change
    description: A change that is about a single ontology element.
    slots:
      - old_value
      - new_value

  ComplexChange:
    is_a: Change
    description: A change that is a composition of other changes.
    slots:
      - change_set

  # Several dozens of other subclasses (direct or indirect) of Change,
  # representing various specialized types of change...

Calling the Java generator with --visitor Change (on the command line; visitors=["Change"] when calling the serialize method) will cause the generator to

(a) create a IChangeVisitor interface containing a visit method for each subclass of Change (and for Change itself):

public interface IChangeVisitor {
    public void visit(Change visited);
    public void visit(SimpleChange visited);
    public void visit(ComplexChange visited);
    /* and so on for all other subclasses... */
}

(b) add a accept(IChangeVisitor) method to the Change class and to all its subclasses, e.g. in SimpleChange.java:

public class SimpleChange extends Change {

    /* Normal code generated for the SimpleChange class... */

    public void accept(IChangeVisitor visitor) {
        visitor.visit(this);
    }
}

Template Variants

The Java generator offers different templates allowing to generate different “flavors” of Java code to represent the same LinkML schema.

A set of template (hereafter called a “template variant”) is selected on the command line by the --template-variant option, or in Python code by the template_variant named parameter to the serialize method.

LinkML currently provides three Java template variants:

  • the default variant;

  • the records variant;

  • and the org.incenp.linkml variant.

Default Variant

The default template variant (which is used when no other variant is explicitly requested) generates Java classes that use Project Lombok’s @Data annotations to provide getters, setters, equals and hashcode functionality.

Records Variant

The records variant represents LinkML classes as Java Record classes, which are intended to hold immutable data.

Note that Record classes are only available since Java 14 as a feature preview, and as an official feature since Java 16.

Also note that a Record class cannot extend another class. If a class Bar is defined in a LinkML schema as extending a class Foo, the records variant will generate a Java Bar class that will contain all the slots from the Foo class but that will not be a subclass of Foo (meaning for example that it will not be possible to assign an instance of Bar to a Foo-typed slot). This makes the records variant unlikely to be suitable for schemas that have complex class hierarchies.

org.incenp.linkml Variant

The org.incenp.linkml variant generates Java code that is suitable for use with the LinkML-Java runtime library – that is, code that meets the requirements set forth in the runtime documentation.

This allows to use said runtime to easily load data (conformant to the LinkML schema from which the code was generated) from files into the Java in-memory representation, and conversely to dump data from the Java representation into files.

Template Selection Logic

The template selection logic used by the Java generator allows to fine-tune which template is used for any given class or enum.

When generating the code for a given class Foo, and assuming a template variant V has been requested (with --template-variant=V), the generator will look up for the following template files, using the first one that it finds:

  • Foo-V.jinja2 (the V variant template specific for the Foo class);

  • class-V.jinja2 (the generic V variant template for all classes);

  • Foo.jinja2 (default template specific for the Foo class);

  • class.jinja2 (generic default template for all classes).

When no variant is explicitly requested, the first two lookups are skipped, meaning the generator will look up first for Foo.jinja2 and then for class.jinja2.

When the --true-enums option is enabled, the same logic will also be used to find the template file to render an enumeration Bar:

  • Bar-V.jinja2 (the V variant template specific for the Bar enumeration);

  • enum-V.jinja2 (the generic V variant template for all enumerations);

  • Bar.jinja2 (default template specific for the Bar enumeration);

  • enum.jinja2 (generic default template for all enumerations).

By default, all templates are looked up in the generator’s internal template directory. Use the --template-dir=D option to make the generator look up first in the specified directory D; any template file found in that directory will take precedence over the templates from the internal directory.

Lastly, use the --template-file=F option to force the generator to always use the specified template. This overrides all the logic described above.

Examples

Alternate Template Example

Here is an alternate template using Hibernate JPA annotations, named example_template.java.jinja2:

package {{ doc.package }};

import java.util.List;
import lombok.*;
import javax.persistence.*;
import org.hibernate.search.engine.backend.types.*;
import org.hibernate.envers.Audited;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.*;


@Audited
@Indexed
@Entity
@Data @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
public class {{ cls.name }} {% if cls.is_a -%} extends {{ cls.is_a }} {%- endif %} {
{% for f in cls.fields %}
  private {{f.range}} {{ f.name }};
{%- endfor %}

}

The alternate template for the generator can be specified with the --template-file option:

linkml generate java --package org.biolink.model \
                     --output-directory org/biolink/model \
                     --template-file example_template.java.jinja2 \
                     biolink-model.yaml