The surface of dev.disarm:disarm and dev.disarm:disarm-kotlin, and how it
lines up with the names used in the other bindings. For install, the JDK floor
and the bundled platforms, see Getting started.
This page covers what is specific to the JVM. Behaviour is language-neutral and lives once, in the concept and user-guide pages.
Disarm is a final class of static methods. disarm-kotlin adds top-level
extension functions over the same native core, so the choice is a matter of which
reads better in your codebase rather than which is capable.
| Java | Kotlin | |
|---|---|---|
| entry point | Disarm.transliterate(text) |
text.transliterate() |
| wide calls | options builders | default arguments |
| naming | camelCase |
camelCase |
| package | dev.disarm |
dev.disarm.kotlin for functions, dev.disarm for types |
Kotlin's functions are top-level. There is no Disarm object to call them on,
and getPipeline is a top-level function too.
import dev.disarm.TargetScript // types
import dev.disarm.kotlin.* // functionsMeasured against the 112 canonical operations in generated/parity.yaml, the JVM
covers 65. Some of the remainder are deprecated aliases or Python-only
conveniences and are not gaps at all. These are the ones that are:
| absent | what it is | reach it with |
|---|---|---|
escapeHtml, percentEncode |
output encoders | your framework's encoder, which you should prefer anyway |
stripLogInjection |
neutralize a log line | canonicalize plus your own newline handling |
decodeToUtf8, detectEncoding |
encoding recovery | — |
registerLang, registerReplacements |
runtime registration | — |
setEmojiProvider |
custom emoji naming | — |
isAscii |
a predicate | text.chars().allMatch(c -> c < 128) |
CVE Validation measures that canonicalize_strict
and strip_obfuscation are the two presets that clear every row of the matrix, and
recommends them on that basis. Both are on the JVM, as canonicalizeStrict and
stripObfuscation; canonicalize misses the eclipsing mark in CVE-2017-7833.
The introspection lists are here too: listLangs, listProfiles and reverseLangs
return what every other binding returns (#981), and each profile's
Pipeline.purpose() says what it is for.
The figures and the table are gated. tests/test_jvm_api_page.py reads the coverage
figure off the parity matrix, which has carried java and kotlin columns since
#677, and fails if the table names a method that Disarm.java or the Kotlin
functions declare.
Four, all with the same shape: a static builder(), chained setters, and
build().
TransliterateOptions.builder().scheme(Scheme.STRICT_ISO9).lang("ru").build();
SlugOptions.builder()
.separator("_").lowercase(true).maxLength(64)
.wordBoundary(true).saveOrder(true).stopwords(List.of("the"))
.allowUnicode(false).lang("de")
.build();
SanitizeFilenameOptions.builder()
.separator("_").maxLength(255).platform(Platform.WINDOWS)
.lang("de").preserveExtension(true)
.build();
MlNormalizeOptions.builder().lang("de").emojiStyle("cldr").foldCase(false).build();Kotlin passes the same values as named arguments and does not use the builders.
| type | what it carries |
|---|---|
AnomalyReport |
anomalous, kinds, findings, reason |
Finding |
one anomaly: kind, token, start, end, detail, reason |
HostnameAnalysis |
suspicious, canonical, scripts, mixedScript, hasConfusables, bidiConflict, bidiControl, hasInvisible, compatFold, crossLabelScript, labelScripts, wholeScriptConfusable, labelWholeScriptConfusable |
KeyCollision |
key, values, indices |
UnmappedConfusable, Untranslatable |
coverage residue |
LangMeta, ScriptMeta, AutoLangInspection |
metadata |
Lexicon, Pipeline |
native handles, AutoCloseable |
TargetScript, NormalizationForm, DigitPolicy, Platform |
enums |
Scheme |
nested in TransliterateOptions; Kotlin aliases it as Scheme |
The other bindings use snake_case; the JVM uses camelCase. Everything else is
the same name, with three exceptions worth knowing:
| elsewhere | JVM |
|---|---|
is_suspicious_hostname → (bool, analysis) in Python |
isSuspiciousHostname → boolean, and analyzeHostname → HostnameAnalysis |
has_anomalies(text) |
hasAnomalies(text, words) — no single-argument form |
Disarm.canonicalize(...) in Java |
"...".canonicalize() in Kotlin |
The hostname split is the one that catches people. Python returns the verdict and
the analysis together; the JVM has a predicate and a separate analysis call, so
asking for both means two calls or one call to analyzeHostname and reading
.suspicious() off it.
DisarmException (extends RuntimeException)
└── DisarmInvalidArgumentException
Unchecked, so nothing forces a try. DisarmInvalidArgumentException is thrown
for a value the library can name as wrong (an unknown profile, an unsupported
scheme) and carries the offending value and the valid set in its message.
Every public Kotlin function with a default argument carries @JvmOverloads, so
each default emits a real JVM method instead of a synthetic $default bridge.
Without it, adding a default to a shipped function deletes an arity that existed,
and callers who have not recompiled meet a NoSuchMethodError at run time.
JvmSignatureTest pins the arities in CI. See
BINDINGS.md for why this is a
guarantee rather than a style.
Disarm.skeletonKey(text[, digitPolicy]) · Disarm.editDistance(a, b) · Disarm.nearestMatch(value, candidates[, maxDistance])
skeletonKey is the TR39 identifier skeleton plus the two prototype classes disarm's table
keeps apart (#650) — a spoof key, never for display; DigitPolicy.TR39 adds the digit half.
editDistance is the Levenshtein distance in characters, and nearestMatch returns the
closest candidate as a NearestMatch(value, distance) record or null beyond
maxDistance (default 1) (#894). The six key builders — canonicalize,
canonicalizeStrict, stripObfuscation, searchKey, sortKey, catalogKey — take a
trailing DigitPolicy overload (#896), and Pipeline.withDigitPolicy(policy) returns a
copy folding under it (#646). Kotlin exposes the same as extension functions with default
arguments.