A Java 25 reimplementation of the core of the
odxtools ODX/PDX diagnostic-database
library: PDX/ODX reading, the data model, ODXLINK/SNREF resolution, value inheritance,
and the DOP / parameter / bit-level encode-decode loop — exposed through a small,
Java-idiomatic public API.
This is a derivative work of the upstream Python odxtools, which remains the
behavior oracle for the port. See NOTICE for attribution and
LICENSE for terms (MIT, same as upstream).
Project status: early / experimental (
0.x). The API may change between releases, and the project is not yet published to a Maven repository — for now, build from source (see Build & test). Contributions are welcome; please readCONTRIBUTING.mdfirst, especially the rule that this port deliberately mirrors upstream Pythonodxtools.
Scope. This repository contains the
odxtools-coremodule only. The CLI, the model-based writer, and the ISO-TP/CAN snoop layer that exist in the full rewrite are not included here, and neither is the Python oracle or its golden-corpus compatibility harness. Some javadoc refers to that "golden corpus" — it describes the validation methodology (semantic and byte-level alignment against the Python implementation), not code shipped in this repo.
- JDK 25 — the build pins a Java 25 toolchain.
The Gradle wrapper is committed; from the repository root:
./gradlew :odxtools-core:test # run the test suite
./gradlew :odxtools-core:check # tests + checkstyle + coverage gate
./gradlew :odxtools-core:jar # build the library jarIf your default JDK is older than 25, point the toolchain at a JDK 25 install, e.g. on macOS:
export JAVA_HOME="$(/usr/libexec/java_home -v 25)"Release archives are reproducible: jar strips embedded timestamps and pins
entry order, so rebuilds of the same sources are byte-identical.
import dev.opendota.odxtools.OdxDatabase;
import dev.opendota.odxtools.LoadOptions;
import dev.opendota.odxtools.diag.DiagLayer;
import dev.opendota.odxtools.diag.DiagService;
import dev.opendota.odxtools.diag.Message;
import dev.opendota.odxtools.value.OdxValue;
import java.nio.file.Path;
import java.util.Map;
OdxDatabase db = OdxDatabase.loadPdx(Path.of("examples/somersault.pdx"));
DiagLayer ecu = db.diagLayers().byShortName("somersault_lazy");
DiagService service = ecu.services().byShortName("do_forward_flips");
byte[] request = service.encodeRequest(Map.of(
"forward_soberness_check", OdxValue.of(0x12),
"num_flips", OdxValue.of(5)));
for (Message msg : ecu.decode(request)) {
System.out.println(msg.service().shortName() + " -> " + msg.values());
}Strictness is per-load via LoadOptions (there is no global mutable state,
so concurrent loads never share strictness):
OdxDatabase db = OdxDatabase.loadPdx(path, LoadOptions.builder()
.strict(false) // tolerate non-conformant data, downgrade to warnings
.schemaValidation(true) // optional W3C XSD validation against the bundled ODX schema
.warningHandler(WarningHandler.collecting())
.build());The full public surface is documented as javadoc on the types under
dev.opendota.odxtools (OdxDatabase, DiagLayer, DiagService, OdxValue,
LoadOptions).
Issues and pull requests are welcome. Please start with
CONTRIBUTING.md — note that this project is a deliberate
class-for-class port of Python odxtools, so changes are expected to track
upstream behavior. By participating you agree to the
Code of Conduct. For security issues, see
SECURITY.md.
MIT — see LICENSE. The bundled ASAM ODX XML schemas under
odxtools-core/src/main/resources/schema/ are governed by ASAM's own terms;
see NOTICE.