Files
Radixor/gradle/paicehusk-benchmarks.gradle
Leo Galambos 38620d7e71 feat: prepare Radixor 3.0.0 with contracted tries and compiled patch commands
Introduce contracted compiled patch tries for faster lookup, make compiled
patch commands the primary runtime path, refresh stemmer benchmarks and
documentation, and restructure the documentation for 3.0.0 onboarding.

BREAKING CHANGE: Radixor 3.0.0 promotes compiled patch-command APIs and
new compiled trie artifacts as the primary runtime integration model.
2026-07-03 18:44:39 +02:00

220 lines
7.5 KiB
Groovy

import org.gradle.plugins.ide.eclipse.model.SourceFolder
def paicehuskVersion = 'master'
def paicehuskArchiveName = "paice-husk-stemmer-${paicehuskVersion}.zip"
def paicehuskDownloadUrl = "https://github.com/Hopper262/paice-husk-stemmer/archive/refs/heads/${paicehuskVersion}.zip"
def paicehuskDownloadFile = layout.buildDirectory.file("third-party/paicehusk/${paicehuskArchiveName}")
def paicehuskExtractDirectory = layout.buildDirectory.dir('third-party/paicehusk/source')
def paicehuskArchiveDirectory = paicehuskExtractDirectory.map { it.dir('paice-husk-stemmer-master') }
def paicehuskJavaFile = paicehuskArchiveDirectory.map { it.file('paicehusk_java.java') }
def paicehuskRulesFile = paicehuskArchiveDirectory.map { it.file('paicehusk_rules.txt') }
def paicehuskGeneratedSourceDirectory = layout.buildDirectory.dir('generated/sources/paicehusk')
def paicehuskGeneratedStemmerFile = paicehuskGeneratedSourceDirectory.map { it.file('org/egothor/stemmer/benchmark/PaiceHuskLancasterStemmer.java') }
def paicehuskGeneratedSourcePath = provider {
project.relativePath(paicehuskGeneratedSourceDirectory.get().asFile)
}
def paicehuskSourceEclipseClasspathAttributes = [
gradle_scope : 'jmh',
gradle_used_by_scope: 'jmh',
test : 'true'
]
def paicehuskIsAbsolutePath = { String path ->
path.startsWith('/') || path ==~ /^[A-Za-z]:[\\\/].*/
}
def paicehuskGeneratedNotice = '''
/**
* Generated at benchmark execution time from upstream
* https://github.com/Hopper262/paice-husk-stemmer .
*
* This source copy is compiled only for the JMH benchmark source set and is
* not committed as production code.
*/
'''
def escapeForJava = { final String text ->
return text.replace('\\\\', '\\\\\\\\')
.replace('\"', '\\\"')
}
def toRuleLines = { final File rulesFile ->
final List<String> lines = rulesFile.readLines('UTF-8')
final StringBuilder ruleLines = new StringBuilder()
for (int index = 0; index < lines.size(); index++) {
final String line = lines.get(index)
ruleLines.append(' "')
ruleLines.append(escapeForJava(line))
ruleLines.append('"')
if (index < lines.size() - 1) {
ruleLines.append(',')
}
ruleLines.append('\n')
}
return ruleLines.toString()
}
def paicehuskEngineInsertion = { final String ruleLines ->
return """
public static final String[] RULE_LINES = {
${ruleLines}
};
private static final java.util.HashMap RULES = createRulesFromEmbeddedRules();
/**
* Creates benchmark stemmer instance.
*/
public PaiceHuskLancasterStemmer() {
}
/**
* Applies Paice/Husk stemming to one token.
*
* @param token input token
* @return stemmed token
*/
public String stem(final String token) {
if (token == null) {
return null;
}
return stemWord(token, RULES, null);
}
/**
* Loads bundled rule lines directly from the generated benchmark source.
*
* @return initialized rule map
*/
private static java.util.HashMap createRulesFromEmbeddedRules() {
try {
final java.io.File ruleFile = java.io.File.createTempFile("paicehusk-rules", ".txt");
ruleFile.deleteOnExit();
try (java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.FileWriter(ruleFile))) {
for (String line : RULE_LINES) {
writer.println(line);
}
}
return loadRules(ruleFile.getAbsolutePath());
} catch (Exception exception) {
throw new IllegalStateException("Unable to initialize benchmark Paice/Husk rules.", exception);
}
}
"""
}
def transformPaiceHuskSource = { final File sourceFile, final File rulesFile, final File targetFile ->
if (!sourceFile.exists()) {
throw new GradleException("Paice/Husk Java source was not available at ${sourceFile}.")
}
if (!rulesFile.exists()) {
throw new GradleException("Paice/Husk rule file was not available at ${rulesFile}.")
}
final String sourceText = sourceFile.getText('UTF-8')
String transformedText = sourceText
transformedText = 'package org.egothor.stemmer.benchmark;' + '\n\n' + transformedText
transformedText = transformedText.replaceFirst(/(?m)^\s*class\s+PaiceHusk\s*\{/, 'public final class PaiceHuskLancasterStemmer {')
final int packageEnd = transformedText.indexOf('\n', transformedText.indexOf('package org.egothor.stemmer.benchmark;'))
if (packageEnd >= 0) {
transformedText = transformedText.substring(0, packageEnd + 1) + '\n' + paicehuskGeneratedNotice + transformedText.substring(packageEnd + 1)
}
final String marker = '\n} // end class PaiceHusk'
final int markerIndex = transformedText.lastIndexOf(marker)
if (markerIndex < 0) {
throw new GradleException("Unexpected Paice/Husk source structure at ${sourceFile}.")
}
final String replacement = paicehuskEngineInsertion(toRuleLines(rulesFile))
transformedText = transformedText.substring(0, markerIndex) + '\n' + replacement + '\n}'
targetFile.parentFile.mkdirs()
targetFile.text = transformedText
}
tasks.register('downloadPaiceHuskStemmer') {
group = 'build setup'
description = 'Downloads the upstream Paice/Husk benchmark source for dynamic extraction.'
outputs.file(paicehuskDownloadFile)
doLast {
final File targetFile = paicehuskDownloadFile.get().asFile
targetFile.parentFile.mkdirs()
if (!targetFile.exists()) {
new URL(paicehuskDownloadUrl).withInputStream { inputStream ->
targetFile.withOutputStream { outputStream ->
outputStream << inputStream
}
}
}
}
}
tasks.register('extractPaiceHuskStemmer', Copy) {
group = 'build setup'
description = 'Extracts the upstream Paice/Husk benchmark archive.'
dependsOn(tasks.named('downloadPaiceHuskStemmer'))
from(zipTree(paicehuskDownloadFile))
into(paicehuskExtractDirectory)
}
tasks.register('generatePaiceHuskLancasterStemmer') {
group = 'build setup'
description = 'Generates PaiceHuskLancasterStemmer into a benchmark-only generated source directory.'
dependsOn(tasks.named('extractPaiceHuskStemmer'))
inputs.files(paicehuskJavaFile, paicehuskRulesFile)
outputs.file(paicehuskGeneratedStemmerFile)
doLast {
transformPaiceHuskSource(paicehuskJavaFile.get().asFile, paicehuskRulesFile.get().asFile, paicehuskGeneratedStemmerFile.get().asFile)
}
}
sourceSets {
jmh {
java {
srcDir(paicehuskGeneratedSourceDirectory)
}
}
}
tasks.named('compileJmhJava') {
dependsOn(tasks.named('generatePaiceHuskLancasterStemmer'))
}
eclipse {
classpath {
file {
whenMerged { classpath ->
String generatedPath = paicehuskGeneratedSourcePath.get()
classpath.entries.removeAll { entry ->
entry.hasProperty('path') && (
entry.path == generatedPath ||
paicehuskIsAbsolutePath(entry.path)
)
}
SourceFolder paicehuskEntry = new SourceFolder(generatedPath, null)
paicehuskEntry.output = 'bin/jmh'
paicehuskSourceEclipseClasspathAttributes.each { String name, String value ->
paicehuskEntry.entryAttributes[name] = value
}
classpath.entries.add(paicehuskEntry)
}
}
}
}