docs: заполнить README и javadoc публичного API#83
Conversation
Переписан README: назначение библиотеки, связка с BSL Language Server, координаты Maven Central, обзор классов обоих пакетов и примеры использования; добавлены тематические баджи (CI, Maven Central, релиз, Java, лицензия). Проставлен javadoc на всех публичных классах, методах и конструкторах: - Lazy — документированы конструкторы и методы (ранее без javadoc); - Absolute — вместо пустых «полученное значение» описан контракт каноникализации путей/URI; - GenericInterner/StringInterner/CaseInsensitivePattern — раскрыт смысл и потокобезопасность, добавлены явные документированные конструкторы; - package-info пакета utils получил обзорный javadoc; - в downloader дописаны краткие описания там, где были только @param. Сборка javadoc проходит без предупреждений по нашему коду. Closes #4 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K65wcrebLcd3e4nbwgUgiN
WalkthroughИзменены лицензирование и README репозитория, расширена документация публичных утилит и загрузчиков. Основная логика методов не менялась; добавлен явный публичный конструктор ChangesДокументация и лицензирование
Estimated code review effort: 1 (Trivial) | ~5 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
||
| ```kotlin | ||
| dependencies { | ||
| implementation("io.github.1c-syntax:utils:0.9.0") |
There was a problem hiding this comment.
Готово: убрал конкретные версии из сниппетов Gradle и Maven, оставил плейсхолдер VERSION. Актуальная версия и так указана ниже ссылками на релизы и Maven Central (651e8fd).
Generated by Claude Code
There was a problem hiding this comment.
Проверил: в репозитории действительно были и COPYING.LESSER.md (LGPL), и COPYING.md (GPL). Переименовал COPYING.LESSER.md → LICENSE.md, удалил COPYING.md, обновил обе ссылки на лицензию в README (f0d2c4c).
Generated by Claude Code
Вместо конкретной версии в сниппетах Gradle/Maven — плейсхолдер VERSION, чтобы README не устаревал; актуальная версия и так указана ниже ссылками на релизы и Maven Central. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K65wcrebLcd3e4nbwgUgiN
| [](https://central.sonatype.com/artifact/io.github.1c-syntax/utils) | ||
| [](https://github.com/1c-syntax/utils/releases) | ||
| [](https://adoptium.net/) | ||
| [](COPYING.LESSER.md) |
There was a problem hiding this comment.
Проверь актуальность ссылки. Если в репозитории действительно все ещё есть copying lesser, то переименуй его в LICENSE.md и удали COPYING.md
LGPL-текст лицензии переименован в LICENSE.md (распознаётся GitHub и привычнее), COPYING.md (GPL) удалён. Ссылки на лицензию в README обновлены. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K65wcrebLcd3e4nbwgUgiN
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
LICENSE.md (1)
15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winУстраните предупреждения markdownlint для LICENSE.md.
markdownlint-cli2сообщает MD001/MD003 для заголовков. Поскольку файл содержит текст лицензии, предпочтительно добавитьLICENSE.mdв исключения линтера, а не менять форматирование официального текста.Also applies to: 44-44, 49-49, 65-65, 80-80, 127-127, 142-142
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@LICENSE.md` at line 15, Exclude LICENSE.md from the markdownlint-cli2 configuration so its official license headings do not trigger MD001/MD003 warnings; leave the license text and formatting unchanged.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Line 7: Align the README license badge and license section with the Maven POM
license metadata and source-file headers, choosing one intentional LGPL variant
and applying it consistently. Update the Gradle publishing configuration that
generates the POM, including the license name and URL, so the published metadata
matches the selected README and header wording.
In `@src/main/java/com/github/_1c_syntax/utils/Absolute.java`:
- Around line 114-121: Обновите документацию метода path(URI), явно указав, что
параметр uri должен использовать схему file:. Зафиксируйте ожидаемое поведение
для URI другой схемы через `@throws` IllegalArgumentException, не изменяя
реализацию метода.
- Around line 37-59: Сузьте Javadoc класса Absolute и метода uri(URI), не
заявляя каноникализацию произвольных URI или сохранение query/fragment; явно
ограничьте обещания поддерживаемыми файловыми URI и стабильными ключами для них.
Не изменяйте реализацию или добавляйте тесты, поскольку комментарий допускает
исправление только документации.
In
`@src/main/java/com/github/_1c_syntax/utils/downloader/DownloadProgressListener.java`:
- Around line 45-48: Обновите Javadoc метода listener рядом с параметром
bytesRead, явно указав, что callback может быть вызван до первого чтения с
bytesRead == 0. Сохраните описание последующих вызовов и значение totalBytes без
изменений.
In `@src/main/java/com/github/_1c_syntax/utils/Lazy.java`:
- Around line 32-42: Синхронизируйте метод clear() тем же ReentrantLock, который
используется в getOrCompute(Supplier) для maybeCompute: захватывайте lock перед
записью null в value и освобождайте его в корректном блоке finally, чтобы сброс
не конфликтовал с вычислением.
In `@src/main/java/com/github/_1c_syntax/utils/StringInterner.java`:
- Around line 27-48: Update StringInterner.intern(String) so null is normalized
to an empty string before delegating to GenericInterner, ensuring intern(null)
and intern(new String("")) return the same canonical instance. Preserve the
existing behavior for non-null strings and the documented non-null result.
---
Nitpick comments:
In `@LICENSE.md`:
- Line 15: Exclude LICENSE.md from the markdownlint-cli2 configuration so its
official license headings do not trigger MD001/MD003 warnings; leave the license
text and formatting unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4c14c802-2640-4b5a-9692-df6edc4d6049
📒 Files selected for processing (12)
COPYING.mdLICENSE.mdREADME.mdsrc/main/java/com/github/_1c_syntax/utils/Absolute.javasrc/main/java/com/github/_1c_syntax/utils/CaseInsensitivePattern.javasrc/main/java/com/github/_1c_syntax/utils/GenericInterner.javasrc/main/java/com/github/_1c_syntax/utils/Lazy.javasrc/main/java/com/github/_1c_syntax/utils/StringInterner.javasrc/main/java/com/github/_1c_syntax/utils/downloader/BslLanguageServerDownloader.javasrc/main/java/com/github/_1c_syntax/utils/downloader/DownloadProgressListener.javasrc/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.javasrc/main/java/com/github/_1c_syntax/utils/package-info.java
💤 Files with no reviewable changes (1)
- COPYING.md
| [](https://central.sonatype.com/artifact/io.github.1c-syntax/utils) | ||
| [](https://github.com/1c-syntax/utils/releases) | ||
| [](https://adoptium.net/) | ||
| [](LICENSE.md) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,120p'
echo "== README license mentions =="
if [ -f README.md ]; then
nl -ba README.
# ... wait for user result?
# Actually the commentary needs include capability only; I mistakenly added no output? done.Repository: 1c-syntax/utils
Length of output: 1830
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files: README/build/gradle =="
git ls-files | rg '(^|/)(README\.md|build\.gradle\.kts|LICENSE.*|package\.json|pom\.xml)$'
echo "== README license mentions =="
if [ -f README.md ]; then
rg -n -i 'LGPL|GNU|license|licence|spdx|lgpl' README.md -C 2
fi
echo "== Gradle license/pom mentions =="
rg -n -i 'LGPL|GNU|license|licence|spdx|licenseUrl|pom' build.gradle.kts -C 3 || true
echo "== source header/license mentions =="
rg -n -i 'LGPL-3\.0|LGPL|GNU LGPL|SPDX-License-Identifier|GNU Lesser General Public License' -g '!build*' -g '!build' -C 1 -M 8 || trueRepository: 1c-syntax/utils
Length of output: 2397
Синхронизируйте вариант лицензии в README и Maven-метаданных.
Раздел лицензии и бейдж README заявляют LGPL-3.0-or-later, а build.gradle.kts:135-147 публикует в POM GNU LGPL 3 с URL фиксированной LGPL 3.0. Выберите намеренный вариант и обновите соответствующие README, POM/заголовки исходников, чтобы публикация не противоречила опубликованному лицензированию.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` at line 7, Align the README license badge and license section with
the Maven POM license metadata and source-file headers, choosing one intentional
LGPL variant and applying it consistently. Update the Gradle publishing
configuration that generates the POM, including the license name and URL, so the
published metadata matches the selected README and header wording.
| * Приведение файловых путей и URI к каноническому абсолютному виду. | ||
| * | ||
| * <p>Утилита сглаживает различия в записи одного и того же файла, из-за которых он иначе выглядел | ||
| * бы как разные ресурсы: относительные пути разворачиваются в абсолютные, символические ссылки и | ||
| * сегменты {@code .}/{@code ..} схлопываются через каноникализацию файла, регистр и разделители | ||
| * приводятся к виду файловой системы. Для {@code file:}-URI дополнительно нормализуется | ||
| * процентное кодирование спецсимволов и восстанавливается authority, чтобы форма URI совпадала с | ||
| * той, что отдаёт JDK для канонического файла. | ||
| * | ||
| * <p>Благодаря этому URI/путь можно использовать как стабильный ключ (например, документа в | ||
| * рабочей области), не опасаясь, что тот же файл придёт в другой записи. | ||
| */ | ||
| @UtilityClass | ||
| public final class Absolute { | ||
|
|
||
| /** | ||
| * Получение URI из строки | ||
| * Разбирает строковый URI и приводит его к каноническому абсолютному виду. | ||
| * | ||
| * <p>Если строка не является корректным URL, она интерпретируется как {@link URI} и обрабатывается | ||
| * через {@link #uri(URI)}. | ||
| * | ||
| * @param uri - строковое представление URI | ||
| * @return - полученное значение | ||
| * @param uri строковое представление URI | ||
| * @return канонический абсолютный URI |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
rg -n -C3 'Absolute\.(uri|path)\(' src/main/java src/test/javaRepository: 1c-syntax/utils
Length of output: 13624
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Map and inspect the Absolute utility implementation around doc and methods, and relevant tests.
ast-grep outline src/main/java/com/github/_1c_syntax/utils/Absolute.java || true
printf '\n--- Absolute.java relevant sections ---\n'
sed -n '1,180p' src/main/java/com/github/_1c_syntax/utils/Absolute.java | cat -n
printf '\n--- AbsoluteTest.java relevant URI behavior tests ---\n'
sed -n '1,230p' src/test/java/com/github/_1c_syntax/utils/AbsoluteTest.java | cat -nRepository: 1c-syntax/utils
Length of output: 14168
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Probe JDK URI semantics and Absolute helper behavior using only repository source and runtime types
# without running repository project code.
python3 - <<'PY'
from pathlib import Path
p = Path("src/main/java/com/github/_1c_syntax/utils/Absolute.java")
text = p.read_text()
print("contains normalize() call:", "normalize()" in text or "URI.normalize" in text)
print("handles query/fragment explicitly:", any(name in text for name in ("getQuery", "getFragment", "getSchemeSpecificPart", "new URI(")))
PY
# Run focused Java probe in memory for JDK URI behavior and Path.of(URI)/File normalization as strings.
# No repository files are imported or executed; only JDK classes are used.
javac -d /tmp/jdk-uri-probe - <<'JAVA'
import java.net.*;
import java.io.*;
public class UriProbe {
public static void main(String[] args) throws Exception {
String[] inputs = {
"file:///fake?query#frag",
"file:///a/./b/../fake.txt",
"untitled://server/x?query#frag"
};
for (String s : inputs) {
URI u = new URI(s);
String psp = u.getSchemeSpecificPart();
String path = u.getPath();
URI normalized = u.normalize();
System.out.println("INPUT=" + s);
System.out.println(" scheme=" + u.getScheme()
+ " schemeSpecificPart=" + psp
+ " path=" + path
+ " query=" + u.getQuery()
+ " fragment=" + u.getFragment()
+ " normalizedPath=" + normalized.getPath());
}
File f = new File(new URI("file:///C%3A/a/../b?query#frag"));
System.out.println("FILE_URI_TO_FILE=" + f.toString());
}
}
JAVA
java -cp /tmp/jdk-uri-probe UriProbeRepository: 1c-syntax/utils
Length of output: 329
Не описывайте uri(URI) как нормализацию произвольных URI.
Метод кодирует полный getSchemeSpecificPart() и не сохраняет query/fragment отдельно: git://x/path?query#frag превращается в path с закодированными ? и #, а ./.. не схлопываются. Подрядки обещают стабильный ключ только для файловых URI, поэтому сузьте Javadoc до поддерживаемых файловых случаев или поправьте реализацию и покройте edge cases тестами.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/github/_1c_syntax/utils/Absolute.java` around lines 37 -
59, Сузьте Javadoc класса Absolute и метода uri(URI), не заявляя каноникализацию
произвольных URI или сохранение query/fragment; явно ограничьте обещания
поддерживаемыми файловыми URI и стабильными ключами для них. Не изменяйте
реализацию или добавляйте тесты, поскольку комментарий допускает исправление
только документации.
| /** | ||
| * Получение пути (path) из URI | ||
| * Возвращает канонический абсолютный путь к файлу, на который указывает URI. | ||
| * | ||
| * @param uri - исходное значение URI | ||
| * @return - полученное значение | ||
| * @param uri исходный URI | ||
| * @return канонический абсолютный путь | ||
| */ | ||
| public static Path path(URI uri) { | ||
| return path(Path.of(uri(uri))); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
rg -n -C3 'path\(URI uri\)|Path\.of\(uri' src/main/java src/test/javaRepository: 1c-syntax/utils
Length of output: 794
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== Candidate files =="
fd -a 'Absolute\.java$' . | sed 's#^\./##'
echo "== Absolute.java relevant sections =="
sed -n '1,170p' src/main/java/com/github/_1c_syntax/utils/Absolute.java
echo "== References to Absolute.path =="
rg -n -C3 'Absolute\.path|\.path\(Path\.(of)?\(|path\(' src/main/java src/test/java | sed -n '1,200p'
echo "== Java Path.of URI documentation excerpt =="
python3 - <<'PY'
# Programmatic source-level check: show source string for Path.of(String) and Uri pattern requirements.
print("Javadoc/source context is not available from repository; rely on Java API contract.")
PYRepository: 1c-syntax/utils
Length of output: 12087
🌐 Web query:
Java Path.of(URI) file URI scheme IllegalArgumentException javadoc
💡 Result:
The Path.of(URI) method in Java throws an IllegalArgumentException if the provided URI does not meet specific requirements associated with the available file system providers [1][2]. The method works by iterating over all installed FileSystemProviders to locate one that matches the URI's scheme [1][3]. If a suitable provider is found, it is then tasked with converting the URI into a Path [1][2]. For the default file system provider, which handles the "file" URI scheme, the following preconditions must be met [1][4]: 1. The URI must have a non-empty path component [1][5]. 2. The URI must have undefined query and fragment components [1][2]. If the URI scheme is not recognized (i.e., no installed provider supports it), or if the URI fails to meet the specific requirements of the chosen provider (such as the default provider's requirement for a non-empty path and absence of query/fragment components), an IllegalArgumentException is thrown [1][6][4]. Additionally, if a URI has no scheme at all, the method will fail because the scheme is required to identify the correct provider [7].
Citations:
- 1: https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/nio/file/Path.html
- 2: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/nio/file/Path.html
- 3: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/Path.html
- 4: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Path.html
- 5: https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/nio/file/Path.html
- 6: https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/nio/file/Paths.html
- 7: https://stackoverflow.com/questions/38065320/java-paths-geturi-throws-illegalargumentexception-but-paths-geturi-tostring
Укажите схему file: в документации path(URI).
Path.of(URI) ожидает URI в файловом пространстве имён; URI другой схемы приведут к IllegalArgumentException. Добавьте это требование в @param, либо опишите как поддерживаемый случай в @throws.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/github/_1c_syntax/utils/Absolute.java` around lines 114 -
121, Обновите документацию метода path(URI), явно указав, что параметр uri
должен использовать схему file:. Зафиксируйте ожидаемое поведение для URI другой
схемы через `@throws` IllegalArgumentException, не изменяя реализацию метода.
| * Вызывается при поступлении очередной порции скачанных байт. | ||
| * | ||
| * @param bytesRead сколько байт ассета уже скачано | ||
| * @param totalBytes полный размер ассета в байтах или {@code -1}, если сервер его не сообщил |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Документируйте начальный вызов с bytesRead == 0.
copyToFile() вызывает listener до первого чтения, передавая 0. Добавьте это в описание, чтобы реализации callback не предполагали, что первый вызов уже отражает скачанные байты.
Предлагаемая формулировка
- * Вызывается при поступлении очередной порции скачанных байт.
+ * Вызывается перед началом скачивания с {`@code` bytesRead == 0} и после каждой
+ * поступившей порции скачанных байт.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * Вызывается при поступлении очередной порции скачанных байт. | |
| * | |
| * @param bytesRead сколько байт ассета уже скачано | |
| * @param totalBytes полный размер ассета в байтах или {@code -1}, если сервер его не сообщил | |
| * Вызывается перед началом скачивания с {`@code` bytesRead == 0} и после каждой | |
| * поступившей порции скачанных байт. | |
| * | |
| * `@param` bytesRead сколько байт ассета уже скачано | |
| * `@param` totalBytes полный размер ассета в байтах или {`@code` -1}, если сервер его не сообщил |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/github/_1c_syntax/utils/downloader/DownloadProgressListener.java`
around lines 45 - 48, Обновите Javadoc метода listener рядом с параметром
bytesRead, явно указав, что callback может быть вызван до первого чтения с
bytesRead == 0. Сохраните описание последующих вызовов и значение totalBytes без
изменений.
| * Хранилище значения с ленивым однократным вычислением и потокобезопасным доступом. | ||
| * | ||
| * <p>Значение вычисляется не в момент создания, а при первом обращении через | ||
| * {@link #getOrCompute()} / {@link #getOrCompute(Supplier)} и кэшируется. Вычисление защищено | ||
| * блокировкой и выполняется по схеме double-checked locking: конкурентные потоки, попавшие на | ||
| * невычисленное значение, ждут единственного вычисления, а не запускают его повторно. Уже | ||
| * вычисленное значение читается без блокировки через {@code volatile}-поле. | ||
| * | ||
| * <p>Вычисленное значение не может быть {@code null}: если {@link Supplier} вернёт {@code null}, | ||
| * будет брошено {@link NullPointerException}. Кэш можно сбросить через {@link #clear()}, | ||
| * после чего следующее обращение вычислит значение заново. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
rg -n -C4 'void clear|lock\.lock|maybeCompute' src/main/java/com/github/_1c_syntax/utils/Lazy.javaRepository: 1c-syntax/utils
Length of output: 835
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "## Lazy.java excerpt"
sed -n '1,180p' src/main/java/com/github/_1c_syntax/utils/Lazy.java
echo
echo "## Deterministic interleaving probe for clear() / getOrCompute() race"
python3 - <<'PY'
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor
import threading, time
`@dataclass`
class State:
value = None
lock = threading.Lock()
calls = 0
nps = 0 # number of non-null values produced
def maybe_compute():
# model current clear/set interleaving where lock is not on clear
if State.value is None:
with State.lock:
# clear can also write null without this lock elsewhere;
# here we only cover a case where both writes interleave.
State.calls += 1
v = "value"
State.nps += 1
State.value = v
return v
return State.value
def clear():
State.value = None
def get_or_compute():
result = State.value
if result is None:
return maybe_compute()
return result
def run():
with ThreadPoolExecutor(max_workers=4) as e:
for _ in range(1000):
futures = [
e.submit(get_or_compute),
e.submit(clear),
e.submit(clear),
]
for f in futures:
f.result()
for i in range(20):
State.calls = 0
State.nps = 0
run()
print(State.calls, State.nps)
PYRepository: 1c-syntax/utils
Length of output: 5186
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
class State:
value = None
lock = False
calls = 0
def maybe_compute():
if State.value is None:
State.lock = True
State.calls += 1
State.value = "value"
State.lock = False
return State.value
def clear():
State.value = None
futures = []
def run():
# Simulate one interleaving with a common ReentrantLock semantics:
# clear can perform its write at the boundary where lock is held only around maybeCompute().
# That interleaving matches the race described.
v1 = State.value # null
State.value = None # clear() interleaves before maybe_compute acquires/holds lock
return maybe_compute()
for _ in range(20):
State.value = None
State.lock = False
State.calls = 0
run()
print(State.calls, State.value)
# Deterministic Java source interleaving expressed in comments here cannot call repository code,
# but the above model captures value being cleared while getOrCompute has not yet
## hold ReentrantLock or is not inside the critical section for the write back.
PY
echo
echo "## Test/API references for Lazy clear/getOrCompute concurrency"
rg -n "getOrCompute\\(|clear\\(|Lazy<T>|class Lazy|JLS" --glob '!build/**' --glob '!out/**' --glob '!target/**' .Repository: 1c-syntax/utils
Length of output: 3448
Синхронизируйте clear() с вычислением.
В getOrCompute(Supplier) блокировка используется только для maybeCompute, а clear() пишет в value без lock. При конкурентном getOrCompute() и clear() сброс может перезаписать уже вычисленное значение или сделать незавершённое вычисление потерянным. Закройте clear() тем же ReentrantLock.
Предлагаемое исправление
public void clear() {
+ lock.lock();
+ try {
value = null;
+ } finally {
+ lock.unlock();
+ }
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/github/_1c_syntax/utils/Lazy.java` around lines 32 - 42,
Синхронизируйте метод clear() тем же ReentrantLock, который используется в
getOrCompute(Supplier) для maybeCompute: захватывайте lock перед записью null в
value и освобождайте его в корректном блоке finally, чтобы сброс не конфликтовал
с вычислением.
| * Интернер строк — {@link GenericInterner} для {@link String}, дополнительно принимающий | ||
| * {@code null}. | ||
| * | ||
| * <p>В отличие от базового интернера, {@code null} не сохраняется, а нормализуется в пустую | ||
| * строку, поэтому метод {@link #intern(String)} никогда не возвращает {@code null}. Для непустых | ||
| * строк поведение совпадает с {@link GenericInterner}. | ||
| */ | ||
| public class StringInterner extends GenericInterner<String> { | ||
|
|
||
| /** | ||
| * Создаёт пустой интернер строк. | ||
| */ | ||
| public StringInterner() { | ||
| // no additional state | ||
| } | ||
|
|
||
| /** | ||
| * Возвращает канонический экземпляр переданной строки; для {@code null} возвращает пустую строку. | ||
| * | ||
| * @param object интернируемая строка либо {@code null} | ||
| * @return канонический экземпляр строки, либо {@code ""}, если передан {@code null} | ||
| */ |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Канонизируйте пустую строку при обработке null.
Сейчас intern(null) напрямую возвращает "", не проходя через GenericInterner. Поэтому intern(null) и intern(new String("")) могут вернуть разные экземпляры, несмотря на заявленную каноникализацию.
Предлагаемое исправление
public String intern(`@Nullable` String object) {
- if (object == null) {
- return "";
- }
- return super.intern(object);
+ return super.intern(object == null ? "" : object);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/github/_1c_syntax/utils/StringInterner.java` around lines
27 - 48, Update StringInterner.intern(String) so null is normalized to an empty
string before delegating to GenericInterner, ensuring intern(null) and
intern(new String("")) return the same canonical instance. Preserve the existing
behavior for non-null strings and the documented non-null result.



Что сделано
README
Вместо одной строки — полноценный документ:
io.github.1c-syntax:utils) со сниппетами для Gradle и Maven;utilsиutils.downloader) с примерами использования;Javadoc публичного API
Проставлен javadoc на всех публичных классах, методах и конструкторах:
Lazy— документированы конструкторы и методы (ранее без javadoc): описаны схема double-checked locking, контракт non-null, сброс кэша;Absolute— вместо пустых «полученное значение» описан контракт каноникализации путей/URI;GenericInterner/StringInterner/CaseInsensitivePattern— раскрыты смысл, потокобезопасность и нюансnull → ""; добавлены явные документированные конструкторы;package-infoпакетаutilsполучил обзорный javadoc;downloaderдописаны краткие summary там, где были только@param.Проверка
./gradlew javadocсобирается без предупреждений по нашему коду (остаются лишь два внешних warning'а про модули gson/semver4j, не связанные с изменениями).Функциональный код не менялся — только документация и явные (документированные) конструкторы вместо неявных default.
Closes #4
🤖 Generated with Claude Code
https://claude.ai/code/session_01K65wcrebLcd3e4nbwgUgiN
Generated by Claude Code