diff --git a/.github/skills/branch-review.md b/.github/skills/branch-review.md new file mode 100644 index 00000000..aeb4467b --- /dev/null +++ b/.github/skills/branch-review.md @@ -0,0 +1,83 @@ +# Skill: Branch Review + +## Purpose +Review the current branch against `develop` and report only material issues with high signal: bugs, regressions, API breaks, resource/concurrency risks, and behavior changes. + +## When to Use +- Before opening a pull request to `develop` +- After major refactoring or API changes +- When you want a strict, impact-focused review + +## Review Scope (RLib-specific) +1. **Public API compatibility first** + - Treat removals/signature changes in public API as high priority by default + - If branch is intentionally alpha-breaking, mark these as accepted and continue + +2. **Prioritize API surface over internals** + - Start with changed files in `src/main/java` + - Prioritize non-`impl/` packages + - Review `impl/` changes when they can affect exposed behavior + +3. **Javadoc quality is part of API review** + - Check public API javadocs for: + - `@since 10.0.0` + - Active voice descriptions + - No trailing periods in `@param`/`@return` + - No unnecessary getter/setter javadocs + +4. **Collections performance pattern** + - Verify empty-state fast paths (`isEmpty()`) in collection operations + - Ensure no unnecessary allocation/iteration when collections are empty + +5. **Runtime/integration constraints** + - Many integration tests use Testcontainers + - If Docker is unavailable, call out that integration validation is limited + +6. **Testing conventions awareness** + - Prefer AssertJ assertions + - Keep test naming and style aligned with repo conventions + +7. **Version mapping correctness** + - For version-to-name lookups (for example OS distribution naming), verify runtime version normalization before map access + - Confirm behavior for real version formats (for example `10.15.7`, `14.4.1`, `15.1`) with targeted tests + +8. **Path handling in file discovery** + - Ensure file discovery helpers return full/usable paths when downstream code reads files + - Flag bare filename returns that make behavior depend on current working directory + +## Process + +### 1. Gather branch diff +```bash +git --no-pager diff --name-only develop...HEAD +git --no-pager diff --stat develop...HEAD +git --no-pager diff develop...HEAD +``` + +### 2. Triage files +- Group into: + - Public API changes + - Internal behavior/refactoring + - Documentation-only changes + +### 3. Review by risk +- Public API contract changes +- Behavioral logic changes +- Concurrency/resource handling +- Performance-sensitive paths +- Documentation contract gaps (for public API) + +### 4. Report findings +Use this format: +- **Severity:** High / Medium / Low +- **File:** path +- **Issue:** concise technical problem +- **Impact:** why it matters in real usage +- **Fix:** concrete recommendation + +Only include findings that are actionable and materially important. + +## Output Expectations +- If no material issues: explicitly state no material issues found +- If issues exist: provide a short prioritized list +- Do not include style-only or trivial nits diff --git a/.github/skills/generate-branch-summary.md b/.github/skills/generate-branch-summary.md index 564dff92..88e8ca89 100644 --- a/.github/skills/generate-branch-summary.md +++ b/.github/skills/generate-branch-summary.md @@ -37,12 +37,6 @@ Group modifications by their nature: Create a section in `summary.md` with this format: ```markdown -# Branch: - -**Status:** -**Commits:** (e.g., "1 (abc1234 - 'commit message')" or "3 commits") -**Version:** (if applicable) - ## Changes Summary ### (e.g., "New Array API") @@ -87,6 +81,8 @@ Create a section in `summary.md` with this format: - Use bullet points for lists - Use bold (`**`) for emphasis on key terms - Use inline code (`` ` ` ``) for class/method names +- Do not add a `Branch:` section or heading in the summary +- Do not add `Status`, `Commits`, or `Version` metadata lines in the summary ### 6. Output Location Always write to `summary.md` in the repository root, replacing or updating the previous summary section. @@ -97,12 +93,6 @@ If `summary.md` already contains unrelated sections (e.g., documentation of othe ## Example Output ```markdown -# Branch: update-api-part-2 - -**Status:** Ready for review -**Commits:** 1 (1367d9b - "small improvements") -**Version:** 10.0.alpha13 → 10.0.alpha14 - ## Changes Summary ### 1. New Array API: `tryTrimTo(int)` diff --git a/.github/skills/generate-javadoc.md b/.github/skills/generate-javadoc.md index ebd02027..d6dfb730 100644 --- a/.github/skills/generate-javadoc.md +++ b/.github/skills/generate-javadoc.md @@ -37,6 +37,7 @@ When the user asks to: 2. **Function @return** - use "the function result" for consistency 3. **Predicate @return** - use "true if the arguments match the predicate" or "true if the arguments match the predicate, false otherwise" 4. **Supplier @return** - describe what is supplied (e.g., "the char value" not just "a result") +5. **Signature-text alignment** - ensure class-level description and type-parameter text match the exact argument order in `make(...)`/`accept(...)` methods (for example, `A, int, C` must be described as "an object, an int, and another object", while `A, B, int` must be described as "two objects and one int") ### Javadoc Standards Each documented element must include: @@ -129,6 +130,7 @@ public interface IntObjConsumer { - Remove obvious/redundant javadocs (getters, setters, constants) - Remove duplicate `@see` references - Add missing `@since` tags +- Fix swapped functional-interface descriptions so they match the method signature argument order ## Execution Steps @@ -145,9 +147,10 @@ public interface IntObjConsumer { 3. **Read each file** to understand existing documentation state 4. **Add Javadocs** using `replace_string_in_file` tool: - - Add class-level Javadoc with description and `@since` - - Add method-level Javadocs with description and `@since` - - Add `@param`/`@return` only for non-trivial methods + - Add class-level Javadoc with description and `@since` + - Add method-level Javadocs with description and `@since` + - Add `@param`/`@return` only for non-trivial methods + - For functional interfaces, verify description wording against the exact parameter order in method signatures 5. **Validate changes:** ```bash diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index a2e7199f..cdcd061f 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -14,16 +14,16 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@v6 - name: Compile all modules run: ./gradlew testClasses @@ -32,7 +32,7 @@ jobs: run: ./gradlew test - name: Generate test report - uses: mikepenz/action-junit-report@v5 + uses: mikepenz/action-junit-report@v6 if: success() || failure() # always run even if the previous step fails with: report_paths: '**/build/test-results/test/TEST-*.xml' @@ -61,13 +61,13 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Generate and submit dependency graph - uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/dependency-submission@v6 diff --git a/.gitignore b/.gitignore index 729ce077..83e104c4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /**/build /**/out /**/.gradle +/summary.md diff --git a/README.md b/README.md index a1422fc5..b1370787 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ repositories { } ext { - rlibVersion = "10.0.alpha14" + rlibVersion = "10.0.alpha15" } dependencies { diff --git a/build.gradle b/build.gradle index e720f454..413d4a73 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -rootProject.version = "10.0.alpha14" +rootProject.version = "10.0.alpha15" group = 'javasabr.rlib' allprojects { diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java index 13fce9f5..44f7ec90 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java @@ -22,9 +22,8 @@ import javasabr.rlib.io.impl.ReuseBytesInputStream; import javasabr.rlib.io.impl.ReuseBytesOutputStream; import javasabr.rlib.io.util.IoUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; @@ -33,13 +32,12 @@ /** * @author JavaSaBr */ +@CustomLog @Accessors(fluent = true) @Getter(AccessLevel.PROTECTED) @FieldDefaults(level = AccessLevel.PROTECTED) public class ClassPathScannerImpl implements ClassPathScanner { - - protected static final Logger LOGGER = LoggerManager.getLogger(ClassPathScanner.class); - + private static final String CLASS_PATH = System.getProperty("java.class.path"); private static final String PATH_SEPARATOR = File.pathSeparator; private static final String CLASS_EXTENSION = ".class"; @@ -176,10 +174,10 @@ private void loadClass( if (!name.endsWith(CLASS_EXTENSION)) { return; } else if(MODULE_INFO_CLASS.equals(name)) { - LOGGER.debug("Skip loading module-info..."); + log.debug("Skip loading module-info..."); return; } else if(name.startsWith(META_INF_PREFIX)) { - LOGGER.debug("Skip loading META-INF class..."); + log.debug("Skip loading META-INF class..."); return; } @@ -199,19 +197,19 @@ private void loadClass( className = result.toString(); } catch (Exception e) { - LOGGER.warning(name, File.separator, "Incorrect replaced class name:[%s] to java path, used separator:[%s]"::formatted); + log.warn(name, File.separator, "Incorrect replaced class name:[%s] to java path, used separator:[%s]"::formatted); return; } - LOGGER.debug(className, "Try to load class:[%s]"::formatted); + log.debug(className, "Try to load class:[%s]"::formatted); try { container.add(loader().loadClass(className)); } catch (NoClassDefFoundError error) { - LOGGER.warning(className, name, rootPath, file, + log.warn(className, name, rootPath, file, "Can't load class:[%s] with original name:[%s], root folder:[%s] and class file:[%s]"::formatted); - LOGGER.warning(error); + log.warn(error); } catch (Throwable e) { - LOGGER.warning(e); + log.warn(e); } } @@ -220,7 +218,7 @@ private void scanDirectory( MutableArray> classes, MutableArray resources, Path directory) { - LOGGER.debug(directory, "Scanning directory:[%s]"::formatted); + log.debug(directory, "Scanning directory:[%s]"::formatted); try (DirectoryStream stream = Files.newDirectoryStream(directory)) { for (Path file : stream) { @@ -261,15 +259,15 @@ private void scanDirectory( } } catch (IOException ex) { - LOGGER.warning(ex); + log.warn(ex); } } private void scanJar(MutableArray> classes, MutableArray resources, Path jarFile) { - LOGGER.debug(jarFile, "Scanning jar:[%s]"::formatted); + log.debug(jarFile, "Scanning jar:[%s]"::formatted); if (!Files.exists(jarFile)) { - LOGGER.warning(jarFile, "Jar file:[%s] does not exists"::formatted); + log.warn(jarFile, "Jar file:[%s] does not exists"::formatted); return; } @@ -280,10 +278,10 @@ private void scanJar(MutableArray> classes, MutableArray resour try (var jin = new JarInputStream(Files.newInputStream(jarFile))) { scanJarInputStream(classes, resources, rout, rin, buffer, jin); } catch (ZipException e) { - LOGGER.warning(jarFile, "Can't open zip file:[%s]"::formatted); - LOGGER.warning(e); + log.warn(jarFile, "Can't open zip file:[%s]"::formatted); + log.warn(e); } catch (IOException e) { - LOGGER.warning(e); + log.warn(e); } } @@ -317,7 +315,7 @@ private void scanJarInputStream( } private void scanJar(MutableArray> classes, MutableArray resources, InputStream jarFile) { - LOGGER.debug(jarFile, "Scanning jar:[%s]"::formatted); + log.debug(jarFile, "Scanning jar:[%s]"::formatted); var rout = new ReuseBytesOutputStream(); var rin = new ReuseBytesInputStream(); @@ -326,10 +324,10 @@ private void scanJar(MutableArray> classes, MutableArray resour try (var jin = new JarInputStream(jarFile)) { scanJarInputStream(classes, resources, rout, rin, buffer, jin); } catch (final ZipException e) { - LOGGER.warning(jarFile, arg -> "Can't open zip file " + arg); - LOGGER.warning(e); + log.warn(jarFile, arg -> "Can't open zip file " + arg); + log.warn(e); } catch (final IOException e) { - LOGGER.warning(e); + log.warn(e); } } @@ -347,7 +345,7 @@ public void scan(@Nullable Predicate filter) { continue; } - LOGGER.debug(file, "Scanning file:[%s]"::formatted); + log.debug(file, "Scanning file:[%s]"::formatted); var filename = file .getFileName() @@ -363,7 +361,7 @@ public void scan(@Nullable Predicate filter) { this.classes = classes.toArray(ArrayUtils.EMPTY_CLASS_ARRAY); this.resources = resources.toArray(ArrayUtils.EMPTY_STRING_ARRAY); - LOGGER.debug( + log.debug( classes().length, resources().length, "Scanned [%s] classes and [%s] resources"::formatted); diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java index 77055bdc..13ac24d2 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java @@ -14,11 +14,13 @@ import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.Utils; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ +@CustomLog @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class ManifestClassPathScannerImpl extends ClassPathScannerImpl { @@ -50,7 +52,7 @@ protected Array calculateManifestClassPath() { URL url = urls.nextElement(); InputStream is = url.openStream(); if (is == null) { - LOGGER.warning(url, arg -> "not found input stream for the url " + arg); + log.warn(url, arg -> "not found input stream for the url " + arg); continue; } @@ -72,7 +74,7 @@ protected Array calculateManifestClassPath() { } } catch (Exception exc) { - LOGGER.warning(exc); + log.warn(exc); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java index b4db250b..e21a047d 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java @@ -20,10 +20,10 @@ @UtilityClass public class ArrayCollectors { - private static Set CH_ID = + private static final Set CH_ID = unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); - private static Set CH_ID_CONC = unmodifiableSet(EnumSet.of( + private static final Set CH_ID_CONC = unmodifiableSet(EnumSet.of( Characteristics.IDENTITY_FINISH, Characteristics.CONCURRENT)); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java index 590e93ed..63e56c3a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java @@ -168,6 +168,7 @@ public int lastIndexOf(@Nullable Object object) { public void forEach(Consumer action) { @Nullable E[] wrapped = wrapped(); for (int i = 0, limit = size(); i < limit; i++) { + //noinspection DataFlowIssue action.accept(wrapped[i]); } } @@ -189,6 +190,7 @@ public T[] toArray(T[] newArray) { if (newArray.length >= size()) { for (int i = 0, j = 0, length = size(), newLength = newArray.length; i < length && j < newLength; i++) { + //noinspection DataFlowIssue,unchecked newArray[j++] = (T) array[i]; } return newArray; @@ -197,6 +199,7 @@ public T[] toArray(T[] newArray) { Class arrayClass = unsafeCast(newArray.getClass()); Class componentType = unsafeCast(arrayClass.getComponentType()); + //noinspection DataFlowIssue return toArray(componentType); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java index 906bb2de..4e66ea50 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java @@ -142,6 +142,7 @@ public E unsafeRemove(int index) { if (index == lastInex) { if (wrapped.compareAndSet(currentArray, copy)) { + //noinspection DataFlowIssue return currentArray[index]; } continue; @@ -154,6 +155,7 @@ public E unsafeRemove(int index) { } if (wrapped.compareAndSet(currentArray, copy)) { + //noinspection DataFlowIssue return currentArray[index]; } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArgsArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArgsArrayIterationFunctions.java index cd71f735..c7487029 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArgsArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArgsArrayIterationFunctions.java @@ -12,12 +12,11 @@ public record DefaultReversedArgsArrayIterationFunctions(UnsafeArray array @Override public @Nullable E findAny(T arg1, BiPredicate filter) { - @Nullable E[] wrapped = array.wrapped(); int size = array.size(); - for (int i = 0; i < size; i++) { E element = wrapped[i]; + //noinspection DataFlowIssue if (filter.test(arg1, element)) { return element; } @@ -30,6 +29,7 @@ public ReversedArgsArrayIterationFunctions forEach(T arg1, BiConsumer ReversedArgsArrayIterationFunctions forEach(F arg1, S arg2, Tri @Nullable E[] wrapped = array.wrapped(); int size = array.size(); for (int i = 0; i < size; i++) { + //noinspection DataFlowIssue consumer.accept(arg1, arg2, wrapped[i]); } return this; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java index 008a22d3..b47f080e 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java @@ -493,7 +493,7 @@ public T[] toArray(T[] container) { @Nullable E[] items = items(); for (int i = head(), j = 0, limit = tail(); i <= limit; i++) { - //noinspection DataFlowIssue,SingleStatementInBlock,unchecked + //noinspection DataFlowIssue,unchecked container[j++] = (T) items[i]; } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java index 1ce49315..893c1fee 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java @@ -126,6 +126,17 @@ public interface Dictionary extends Iterable { */ MutableArray values(MutableArray container); + /** + * Collects part of values from this dictionary with starting from index with provided soft limit. + * + * @param container the array to add values to + * @param startIndex the index of first value to collect + * @param limit the soft limit of values to collect + * @return the index which can be used as startIndex for next iteration or -1 + * @since 10.0.0 + */ + int values(MutableArray container, int startIndex, int limit); + /** * Returns all values as an immutable array. * diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java index ef8f8998..88749bee 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java @@ -11,11 +11,6 @@ import java.util.stream.Collector; import java.util.stream.Collector.Characteristics; import javasabr.rlib.common.util.ObjectUtils; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import lombok.experimental.FieldDefaults; import lombok.experimental.UtilityClass; /** @@ -28,26 +23,21 @@ public class DictionaryCollectors { static final Set CH_ID = unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); - @Getter - @Accessors(fluent = true) - @RequiredArgsConstructor - @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) - static class CollectorImpl implements Collector { - - Supplier supplier; - BiConsumer accumulator; - BinaryOperator combiner; - Function finisher; - Set characteristics; - - CollectorImpl( - Supplier supplier, - BiConsumer accumulator, - BinaryOperator combiner, - Set characteristics) { - this(supplier, accumulator, combiner, a -> (R) a, characteristics); + record CollectorImpl( + Supplier supplier, + BiConsumer accumulator, + BinaryOperator combiner, + Function finisher, + Set characteristics) implements Collector { + + CollectorImpl( + Supplier supplier, + BiConsumer accumulator, + BinaryOperator combiner, + Set characteristics) { + this(supplier, accumulator, combiner, a -> (R) a, characteristics); + } } - } /** * Returns a collector that accumulates elements into a dictionary using the element as the value. diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/IntToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/IntToRefDictionary.java index 4a198619..c4168d22 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/IntToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/IntToRefDictionary.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.dictionary; +import java.util.Objects; import java.util.Optional; import javasabr.rlib.collections.array.IntArray; import javasabr.rlib.collections.array.MutableIntArray; @@ -80,7 +81,7 @@ static IntToRefDictionary of(int k1, V v1, int k2, V v2) { static IntToRefDictionary ofEntries(IntToRefEntry... entries) { MutableIntToRefDictionary mutable = DictionaryFactory.mutableIntToRefDictionary(); for (var entry : entries) { - mutable.put(entry.key(), entry.value()); + mutable.put(entry.key(), Objects.requireNonNull(entry.value())); } return mutable.toReadOnly(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LongToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LongToRefDictionary.java index 09964c0c..5cbef43a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LongToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LongToRefDictionary.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.dictionary; +import java.util.Objects; import java.util.Optional; import javasabr.rlib.collections.array.LongArray; import javasabr.rlib.collections.array.MutableLongArray; @@ -80,7 +81,7 @@ static LongToRefDictionary of(long k1, V v1, long k2, V v2) { static LongToRefDictionary ofEntries(LongToRefEntry... entries) { MutableLongToRefDictionary mutable = DictionaryFactory.mutableLongToRefDictionary(); for (var entry : entries) { - mutable.put(entry.key(), entry.value()); + mutable.put(entry.key(), Objects.requireNonNull(entry.value())); } return mutable.toReadOnly(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedIntToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedIntToRefDictionary.java index debd484b..85eec3df 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedIntToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedIntToRefDictionary.java @@ -12,8 +12,10 @@ import javasabr.rlib.collections.array.MutableIntArray; import javasabr.rlib.collections.array.UnsafeMutableArray; import javasabr.rlib.collections.array.UnsafeMutableIntArray; +import javasabr.rlib.collections.dictionary.IntToRefDictionary; import javasabr.rlib.collections.dictionary.LinkedHashIntToRefEntry; import javasabr.rlib.collections.dictionary.UnsafeIntToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.util.LinkedEntryUtils; import javasabr.rlib.functions.IntObjConsumer; import org.jspecify.annotations.Nullable; @@ -33,15 +35,13 @@ public boolean containsKey(int key) { @Override public boolean containsValue(V value) { - for (E entry : entries()) { - for (E nextEntry = entry; nextEntry != null; nextEntry = nextEntry.next()) { - if (Objects.equals(value, nextEntry.value())) { + for (E next = entry; next != null; next = next.next()) { + if (Objects.equals(value, next.value())) { return true; } } } - return false; } @@ -92,17 +92,14 @@ public Iterator iterator() { @Nullable protected E findEntry(int key) { - @Nullable E[] entries = entries(); int hash = hash(key); int entryIndex = indexFor(hash, entries.length); - for (E entry = entries[entryIndex]; entry != null; entry = entry.next()) { if (entry.hash() == hash && key == entry.key()) { return entry; } } - return null; } @@ -192,37 +189,46 @@ public Array values(Class type) { @Override public > C values(C container) { - if (isEmpty()) { - return container; - } - for (E entry : entries()) { - while (entry != null) { - V value = entry.value(); - if (value != null) { - container.add(value); - } - entry = entry.next(); - } - } - return container; + return LinkedEntryUtils.values(entries(), size(), container); } @Override public MutableArray values(MutableArray container) { - if (isEmpty()) { - return container; + return LinkedEntryUtils.values(entries(), size(), container); + } + + @Override + public int values(MutableArray container, int startIndex, int limit) { + return LinkedEntryUtils.values(entries(), size(), container, startIndex, limit); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntToRefDictionary another)) { + return false; + } else if (size() != another.size()) { + return false; } - UnsafeMutableArray unsafe = container.asUnsafe(); - unsafe.prepareForSize(container.size() + size()); for (E entry : entries()) { while (entry != null) { + if (!another.containsKey(entry.key())) { + return false; + } V value = entry.value(); - if (value != null) { - unsafe.unsafeAdd(value); + if (!Objects.equals(value, another.get(entry.key()))) { + return false; } entry = entry.next(); } } - return container; + return true; + } + + @Override + public String toString() { + return LinkedEntryUtils.toString( + entries(), + size(), + (builder, entry) -> builder.append(entry.key())); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedLongToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedLongToRefDictionary.java index 07f2937a..7dbbe777 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedLongToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedLongToRefDictionary.java @@ -13,7 +13,9 @@ import javasabr.rlib.collections.array.UnsafeMutableArray; import javasabr.rlib.collections.array.UnsafeMutableLongArray; import javasabr.rlib.collections.dictionary.LinkedHashLongToRefEntry; +import javasabr.rlib.collections.dictionary.LongToRefDictionary; import javasabr.rlib.collections.dictionary.UnsafeLongToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.util.LinkedEntryUtils; import javasabr.rlib.functions.LongObjConsumer; import org.jspecify.annotations.Nullable; @@ -33,15 +35,13 @@ public boolean containsKey(long key) { @Override public boolean containsValue(V value) { - for (E entry : entries()) { - for (E nextEntry = entry; nextEntry != null; nextEntry = nextEntry.next()) { - if (Objects.equals(value, nextEntry.value())) { + for (E next = entry; next != null; next = next.next()) { + if (Objects.equals(value, next.value())) { return true; } } } - return false; } @@ -92,17 +92,14 @@ public Iterator iterator() { @Nullable protected E findEntry(long key) { - @Nullable E[] entries = entries(); int hash = hash(Long.hashCode(key)); int entryIndex = indexFor(hash, entries.length); - for (E entry = entries[entryIndex]; entry != null; entry = entry.next()) { if (entry.hash() == hash && key == entry.key()) { return entry; } } - return null; } @@ -192,37 +189,46 @@ public Array values(Class type) { @Override public > C values(C container) { - if (isEmpty()) { - return container; - } - for (E entry : entries()) { - while (entry != null) { - V value = entry.value(); - if (value != null) { - container.add(value); - } - entry = entry.next(); - } - } - return container; + return LinkedEntryUtils.values(entries(), size(), container); } @Override public MutableArray values(MutableArray container) { - if (isEmpty()) { - return container; + return LinkedEntryUtils.values(entries(), size(), container); + } + + @Override + public int values(MutableArray container, int startIndex, int limit) { + return LinkedEntryUtils.values(entries(), size(), container, startIndex, limit); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof LongToRefDictionary another)) { + return false; + } else if (size() != another.size()) { + return false; } - UnsafeMutableArray unsafe = container.asUnsafe(); - unsafe.prepareForSize(container.size() + size()); for (E entry : entries()) { while (entry != null) { + if (!another.containsKey(entry.key())) { + return false; + } V value = entry.value(); - if (value != null) { - unsafe.unsafeAdd(value); + if (!Objects.equals(value, another.get(entry.key()))) { + return false; } entry = entry.next(); } } - return container; + return true; + } + + @Override + public String toString() { + return LinkedEntryUtils.toString( + entries(), + size(), + (builder, entry) -> builder.append(entry.key())); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java index 4771b91d..e4c0b861 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java @@ -14,6 +14,7 @@ import javasabr.rlib.collections.dictionary.LinkedHashEntry; import javasabr.rlib.collections.dictionary.RefToRefDictionary; import javasabr.rlib.collections.dictionary.UnsafeRefToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.util.LinkedEntryUtils; import org.jspecify.annotations.Nullable; public abstract class AbstractHashBasedRefToRefDictionary> @@ -122,17 +123,14 @@ public MutableArray keys(MutableArray container) { if (isEmpty()) { return container; } - UnsafeMutableArray unsafe = container.asUnsafe(); unsafe.prepareForSize(container.size() + size()); - for (E entry : entries()) { while (entry != null) { unsafe.unsafeAdd(entry.key()); entry = entry.next(); } } - return container; } @@ -147,38 +145,17 @@ public Array values(Class type) { @Override public > C values(C container) { - if (isEmpty()) { - return container; - } - for (E entry : entries()) { - while (entry != null) { - V value = entry.value(); - if (value != null) { - container.add(value); - } - entry = entry.next(); - } - } - return container; + return LinkedEntryUtils.values(entries(), size(), container); } @Override public MutableArray values(MutableArray container) { - if (isEmpty()) { - return container; - } - UnsafeMutableArray unsafe = container.asUnsafe(); - unsafe.prepareForSize(container.size() + size()); - for (E entry : entries()) { - while (entry != null) { - V value = entry.value(); - if (value != null) { - unsafe.unsafeAdd(value); - } - entry = entry.next(); - } - } - return container; + return LinkedEntryUtils.values(entries(), size(), container); + } + + @Override + public int values(MutableArray container, int startIndex, int limit) { + return LinkedEntryUtils.values(entries(), size(), container, startIndex, limit); } @Override @@ -193,17 +170,15 @@ public boolean equals(Object obj) { } else if (size() != another.size()) { return false; } - - RefToRefDictionary toCompare = (RefToRefDictionary) obj; - + @SuppressWarnings("unchecked") + var toCompare = (RefToRefDictionary) obj; for (E entry : entries()) { while (entry != null) { if (!toCompare.containsKey(entry.key())) { return false; } V value = entry.value(); - Object anotherValue = toCompare.get(entry.key()); - if (!Objects.equals(value, anotherValue)) { + if (!Objects.equals(value, toCompare.get(entry.key()))) { return false; } entry = entry.next(); @@ -214,35 +189,9 @@ public boolean equals(Object obj) { @Override public String toString() { - - if (isEmpty()) { - return "[]"; - } - - StringBuilder builder = new StringBuilder("["); - - for (E entry : entries()) { - while (entry != null) { - builder - .append('\'') - .append(entry.key()) - .append('\'') - .append(":") - .append('\'') - .append(entry.value()) - .append('\'') - .append(", "); - - entry = entry.next(); - } - } - - if (builder.length() > 1) { - builder.delete(builder.length() - 2, builder.length()); - } - - builder.append("]"); - - return builder.toString(); + return LinkedEntryUtils.toString( + entries(), + size(), + (builder, entry) -> builder.append(entry.key())); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/LinkedEntryUtils.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/LinkedEntryUtils.java new file mode 100644 index 00000000..7548e24a --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/LinkedEntryUtils.java @@ -0,0 +1,108 @@ +package javasabr.rlib.collections.dictionary.impl.util; + +import java.util.Collection; +import java.util.function.BiFunction; +import javasabr.rlib.collections.array.MutableArray; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.collections.dictionary.LinkedEntry; +import javasabr.rlib.collections.dictionary.RefEntry; +import org.jspecify.annotations.Nullable; + +public class LinkedEntryUtils { + + public static & RefEntry, C extends Collection> C values( + @Nullable E[] entries, + int size, + C container) { + if (size < 1) { + return container; + } + for (E entry : entries) { + while (entry != null) { + V value = entry.value(); + if (value != null) { + container.add(value); + } + entry = entry.next(); + } + } + return container; + } + + public static & RefEntry> MutableArray values( + @Nullable E[] entries, + int size, + MutableArray container) { + if (size < 1) { + return container; + } + UnsafeMutableArray unsafe = container.asUnsafe(); + unsafe.prepareForSize(container.size() + size); + for (E entry : entries) { + while (entry != null) { + V value = entry.value(); + if (value != null) { + unsafe.unsafeAdd(value); + } + entry = entry.next(); + } + } + return container; + } + + public static & RefEntry> int values( + @Nullable E[] entries, + int size, + MutableArray container, + int startIndex, + int limit) { + if (size < 1) { + return -1; + } + UnsafeMutableArray unsafe = container.asUnsafe(); + unsafe.prepareForSize(container.size() + Math.min(limit, size)); + for (int i = startIndex, length = entries.length; i < length; i++) { + if (unsafe.size() >= limit) { + return i; + } + E entry = entries[i]; + while (entry != null) { + V value = entry.value(); + if (value != null) { + unsafe.unsafeAdd(value); + } + entry = entry.next(); + } + } + return -1; + } + + public static & RefEntry> String toString( + @Nullable E[] entries, + int size, + BiFunction keyAppender) { + if (size < 1) { + return "[]"; + } + var builder = new StringBuilder("["); + for (E entry : entries) { + while (entry != null) { + builder.append('\''); + keyAppender + .apply(builder, entry) + .append('\'') + .append(":") + .append('\'') + .append(entry.value()) + .append('\'') + .append(", "); + entry = entry.next(); + } + } + if (builder.length() > 1) { + builder.delete(builder.length() - 2, builder.length()); + } + builder.append("]"); + return builder.toString(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/package-info.java new file mode 100644 index 00000000..7827f60a --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/util/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.dictionary.impl.util; + +import org.jspecify.annotations.NullMarked; diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java index fd613d11..780f62d5 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java @@ -172,6 +172,35 @@ void shouldHaveExpectedResultFromForEach(IntToRefDictionary dictionary) assertThat(pairs).isEqualTo(expectedPairs); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldCheckEqualsCorrectly(IntToRefDictionary dictionary) { + // given: + IntToRefDictionary theSame = IntToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(3, "val3"), + entry(4, "val4"), + entry(5, "val5")); + IntToRefDictionary notTheSame1 = IntToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(33, "val3"), + entry(4, "val4"), + entry(5, "val5")); + IntToRefDictionary notTheSame2 = IntToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(3, "val3"), + entry(4, "val44"), + entry(5, "val5")); + + // when/then: + assertThat(dictionary).isEqualTo(theSame); + assertThat(dictionary).isNotEqualTo(notTheSame1); + assertThat(dictionary).isNotEqualTo(notTheSame2); + } + private static Stream generateDictionaries() { IntToRefDictionary source = IntToRefDictionary.ofEntries( diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/LongToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/LongToRefDictionaryTest.java index 548d36e5..8a6514f2 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/LongToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/LongToRefDictionaryTest.java @@ -172,6 +172,35 @@ void shouldHaveExpectedResultFromForEach(LongToRefDictionary dictionary) assertThat(pairs).isEqualTo(expectedPairs); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldCheckEqualsCorrectly(LongToRefDictionary dictionary) { + // given: + LongToRefDictionary theSame = LongToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(3, "val3"), + entry(4, "val4"), + entry(5, "val5")); + LongToRefDictionary notTheSame1 = LongToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(33, "val3"), + entry(4, "val4"), + entry(5, "val5")); + LongToRefDictionary notTheSame2 = LongToRefDictionary.ofEntries( + entry(1, "val1"), + entry(2, "val2"), + entry(3, "val3"), + entry(4, "val44"), + entry(5, "val5")); + + // when/then: + assertThat(dictionary).isEqualTo(theSame); + assertThat(dictionary).isNotEqualTo(notTheSame1); + assertThat(dictionary).isNotEqualTo(notTheSame2); + } + private static Stream generateDictionaries() { LongToRefDictionary source = LongToRefDictionary.ofEntries( diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java index 47356878..44ac22fb 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.stream.Stream; +import javasabr.rlib.collections.array.ArrayFactory; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -207,6 +208,35 @@ void shouldBeEmptyAfterClear(MutableIntToRefDictionary dictionary) { assertThat(dictionary.containsKey(1)).isFalse(); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldIterateAllValuesUsingPartIndex(MutableIntToRefDictionary dictionary) { + // given: + var expectedValues = ArrayFactory.mutableArray(String.class); + for (int i = 10; i < 1000; i += 8) { + var value = "value_" + i; + expectedValues.add(value); + dictionary.put(i, value); + } + + expectedValues.sort(); + + var accumulatedValues = ArrayFactory.mutableArray(String.class); + var extractedPart = ArrayFactory.mutableArray(String.class); + + // when: + int partIndex = 0; + while (partIndex >= 0) { + extractedPart.clear(); + partIndex = dictionary.values(extractedPart, partIndex, 20); + accumulatedValues.addAll(extractedPart); + } + accumulatedValues.sort(); + + // then: + assertThat(accumulatedValues).isEqualTo(expectedValues); + } + private static Stream generateDictionaries() { return Stream.of( Arguments.of(MutableIntToRefDictionary.ofTypes(String.class)), diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableLongToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableLongToRefDictionaryTest.java index 0920ba4e..be61872a 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableLongToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableLongToRefDictionaryTest.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.stream.Stream; +import javasabr.rlib.collections.array.ArrayFactory; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -207,6 +208,35 @@ void shouldBeEmptyAfterClear(MutableLongToRefDictionary dictionary) { assertThat(dictionary.containsKey(1)).isFalse(); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldIterateAllValuesUsingPartIndex(MutableLongToRefDictionary dictionary) { + // given: + var expectedValues = ArrayFactory.mutableArray(String.class); + for (int i = 10; i < 1000; i += 8) { + var value = "value_" + i; + expectedValues.add(value); + dictionary.put(i, value); + } + + expectedValues.sort(); + + var accumulatedValues = ArrayFactory.mutableArray(String.class); + var extractedPart = ArrayFactory.mutableArray(String.class); + + // when: + int partIndex = 0; + while (partIndex >= 0) { + extractedPart.clear(); + partIndex = dictionary.values(extractedPart, partIndex, 20); + accumulatedValues.addAll(extractedPart); + } + accumulatedValues.sort(); + + // then: + assertThat(accumulatedValues).isEqualTo(expectedValues); + } + private static Stream generateDictionaries() { return Stream.of( Arguments.of(MutableLongToRefDictionary.ofTypes(String.class)), diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableRefToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableRefToRefDictionaryTest.java index 99f28ce6..c6550795 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableRefToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableRefToRefDictionaryTest.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.stream.Stream; +import javasabr.rlib.collections.array.ArrayFactory; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -207,6 +208,36 @@ void shouldAppendDictionary(MutableRefToRefDictionary dictionary assertThat(dictionary.size()).isEqualTo(5); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldIterateAllValuesUsingPartIndex(MutableRefToRefDictionary dictionary) { + // given: + var expectedValues = ArrayFactory.mutableArray(String.class); + for (int i = 10; i < 1000; i += 8) { + var value = "value_" + i; + var key = "key_" + i; + expectedValues.add(value); + dictionary.put(key, value); + } + + expectedValues.sort(); + + var accumulatedValues = ArrayFactory.mutableArray(String.class); + var extractedPart = ArrayFactory.mutableArray(String.class); + + // when: + int partIndex = 0; + while (partIndex >= 0) { + extractedPart.clear(); + partIndex = dictionary.values(extractedPart, partIndex, 20); + accumulatedValues.addAll(extractedPart); + } + accumulatedValues.sort(); + + // then: + assertThat(accumulatedValues).isEqualTo(expectedValues); + } + private static Stream generateDictionaries() { return Stream.of( Arguments.of(MutableRefToRefDictionary.ofTypes(String.class, String.class)), diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryTest.java index 15c21f00..623864aa 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryTest.java @@ -133,7 +133,6 @@ void shouldHaveExpectedValues(RefToRefDictionary dictionary) { @ParameterizedTest @MethodSource("generateDictionaries") void shouldHaveExpectedResultFromForEach(RefToRefDictionary dictionary) { - // given: var expectedArray = Array.typed(String.class, "val4", "val3", "val5", "val2", "val1"); var expectedPairs = Array.typed(Tuple.class, @@ -158,6 +157,35 @@ void shouldHaveExpectedResultFromForEach(RefToRefDictionary dict assertThat(pairs).isEqualTo(expectedPairs); } + @ParameterizedTest + @MethodSource("generateDictionaries") + void shouldCheckEqualsCorrectly(RefToRefDictionary dictionary) { + // given: + RefToRefDictionary theSame = RefToRefDictionary.ofEntries( + entry("key1", "val1"), + entry("key2", "val2"), + entry("key3", "val3"), + entry("key4", "val4"), + entry("key5", "val5")); + RefToRefDictionary notTheSame1 = RefToRefDictionary.ofEntries( + entry("key1", "val1"), + entry("key2", "val2"), + entry("key3", "val3"), + entry("key4", "val46"), + entry("key5", "val5")); + RefToRefDictionary notTheSame2 = RefToRefDictionary.ofEntries( + entry("key1", "val1"), + entry("key21", "val2"), + entry("key3", "val3"), + entry("key4", "val4"), + entry("key5", "val5")); + + // when/then: + assertThat(dictionary).isEqualTo(theSame); + assertThat(dictionary).isNotEqualTo(notTheSame1); + assertThat(dictionary).isNotEqualTo(notTheSame2); + } + private static Stream generateDictionaries() { RefToRefDictionary source = RefToRefDictionary.ofEntries( diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ClassUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ClassUtils.java index 76f94b2c..0f3dd3be 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/ClassUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ClassUtils.java @@ -5,7 +5,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; -import javasabr.rlib.logger.api.LoggerManager; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -144,13 +143,12 @@ private static boolean isCommonType(Class type, Object[] objects, int offset) * @return the class or null. * @since 9.8.0 */ - public static @Nullable Class tryGetClass(String name) { + @Nullable + public static Class tryGetClass(String name) { try { return unsafeCast(forName(name)); } catch (ClassNotFoundException e) { - LoggerManager - .getDefaultLogger() - .warning(e); + Utils.printWarn(e); return null; } } @@ -180,13 +178,12 @@ public static Class getClass(String name) { * @return the constructor or null. * @since 9.8.0 */ - public static @Nullable Constructor tryGetConstructor(Class cs, @Nullable Class... classes) { + @Nullable + public static Constructor tryGetConstructor(Class cs, @Nullable Class... classes) { try { return unsafeCast(cs.getConstructor(classes)); } catch (NoSuchMethodException | SecurityException e) { - LoggerManager - .getDefaultLogger() - .warning(e); + Utils.printWarn(e); return null; } } @@ -200,13 +197,12 @@ public static Class getClass(String name) { * @return the constructor or null. * @since 9.8.0 */ - public static @Nullable Constructor tryGetConstructor(String className, Class... classes) { + @Nullable + public static Constructor tryGetConstructor(String className, Class... classes) { try { return unsafeCast(forName(className).getConstructor(classes)); } catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) { - LoggerManager - .getDefaultLogger() - .warning(e); + Utils.printWarn(e); return null; } } @@ -236,13 +232,11 @@ public static Constructor getConstructor(Class cs, @Nullable Class. * @return true if this class has constructor wth the arguments. */ public static boolean hasConstructor(Class cs, Class... classes) { - for (var constructor : cs.getConstructors()) { if (Arrays.equals(constructor.getParameterTypes(), classes)) { return true; } } - return false; } @@ -253,13 +247,11 @@ public static boolean hasConstructor(Class cs, Class... classes) { * @return true if this class has empty constructor. */ public static boolean hasConstructor(Class cs) { - for (var constructor : cs.getConstructors()) { if (constructor.getParameterCount() == 0) { return true; } } - return false; } @@ -273,7 +265,8 @@ public static boolean hasConstructor(Class cs) { public static T newInstance(Class cs) { try { return unsafeNNCast(cs.getDeclaredConstructor().newInstance()); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | + NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } } @@ -306,8 +299,8 @@ public static T newInstance(Constructor constructor, Object... ob public static T newInstance(String className) { try { return unsafeNNCast(forName(className).getDeclaredConstructor().newInstance()); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | + ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } } @@ -319,7 +312,8 @@ public static T newInstance(String className) { * @param the expected type. * @return the casted object. */ - public static @Nullable T unsafeCast(@Nullable Object object) { + @Nullable + public static T unsafeCast(@Nullable Object object) { return (T) object; } @@ -342,7 +336,8 @@ public static T unsafeNNCast(Object object) { * @param the target type. * @return the casted object. */ - public static @Nullable T unsafeCast(Class type, @Nullable Object object) { + @Nullable + public static T unsafeCast(Class type, @Nullable Object object) { return type.cast(object); } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java b/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java index 7d533ae3..75b72148 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java @@ -53,7 +53,7 @@ public T resolve(int number) { try { return values[number]; } catch (IndexOutOfBoundsException e) { - log.warning(e.getMessage()); + log.warn(e); return null; } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ObjectUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ObjectUtils.java index 26ba5852..897a06f5 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/ObjectUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ObjectUtils.java @@ -46,11 +46,9 @@ public static T notNull(@Nullable T obj) { * @since 9.0.2 */ public static T notNull(@Nullable T obj, Supplier supplier) { - if (obj == null) { throw supplier.get(); } - return obj; } @@ -69,11 +67,9 @@ public static T notNull( @Nullable T obj, F arg, Function factory) { - if (obj == null) { throw factory.apply(arg); } - return obj; } @@ -91,11 +87,9 @@ public static T notNull( @Nullable T obj, long arg, LongFunction factory) { - if (obj == null) { throw factory.apply(arg); } - return obj; } @@ -122,25 +116,15 @@ public static T ifNull(@Nullable T obj, T another) { public static T ifNull(@Nullable T obj, Supplier factory) { return obj == null ? factory.get() : obj; } - - /** - * Gets hash of the boolean value. - * - * @param value the boolean value. - * @return the hash. - */ + + @Deprecated(forRemoval = true) public static int hash(boolean value) { - return value ? 1231 : 1237; + return Boolean.hashCode(value); } - - /** - * Gets hash of the long value. - * - * @param value the long value. - * @return the hash. - */ + + @Deprecated(forRemoval = true) public static int hash(long value) { - return (int) (value ^ value >>> 32); + return Long.hashCode(value); } /** diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/StringUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/StringUtils.java index 8eeadff2..a8b0a208 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/StringUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/StringUtils.java @@ -155,27 +155,18 @@ public static String toString(Throwable throwable) { * @return the stack trace. */ public static String toString(Throwable throwable, int deepLevel) { - var writer = new StringWriter(); var printWriter = new PrintWriter(writer); - throwable.printStackTrace(printWriter); - var stackTrace = new StringBuilder(writer.toString()); - int level = 0; - for (var cause = throwable.getCause(); cause != null && level < deepLevel; cause = cause.getCause(), level++) { - writer = new StringWriter(); printWriter = new PrintWriter(writer); - cause.printStackTrace(printWriter); - stackTrace.append("\n caused by "); - stackTrace.append(writer.toString()); + stackTrace.append(writer); } - return stackTrace.toString(); } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadUtils.java index fa96c3ee..7330df25 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadUtils.java @@ -1,27 +1,29 @@ package javasabr.rlib.common.util; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; +import lombok.CustomLog; /** * Utility methods for thread operations. * * @since 10.0.0 */ +@CustomLog public class ThreadUtils { - - private static final Logger LOGGER = LoggerManager.getLogger(ThreadUtils.class); - + /** * Sleeps the current thread for the specified time, ignoring interrupts. * * @param time the time to sleep in milliseconds + * @return true if it was interrupted + * @since 10.0.0 */ - public static void sleep(long time) { + public static boolean sleep(long time) { try { Thread.sleep(time); + return false; } catch (InterruptedException e) { - LOGGER.warning(e); + log.warn(e); + return true; } } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java index 3cae0fdf..54e0d52d 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java @@ -58,7 +58,7 @@ public static Path getRootFolderFromClass(Class cs) { var uri = new URI(path); path = uri.getPath(); - path = path.replaceAll("%20", " "); + path = path.replace("%20", " "); if (File.separatorChar != '/') { @@ -237,15 +237,27 @@ public static R tryGet( } } - public static void print(Exception exception) { + /** + * Logs warning details from the exception to the default logger. + * + * @param exception the exception to log + * @since 10.0.0 + */ + public static void printWarn(Exception exception) { LoggerManager .getDefaultLogger() - .warning(exception); + .warn(exception); } - public static void print(String message) { + /** + * Logs a warning message to the default logger. + * + * @param message the message to log + * @since 10.0.0 + */ + public static void printWarn(String message) { LoggerManager .getDefaultLogger() - .warning(message); + .warn(message); } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystem.java b/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystem.java index f08ee9bb..ef1d996b 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystem.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystem.java @@ -1,6 +1,5 @@ package javasabr.rlib.common.util.os; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** @@ -8,55 +7,9 @@ * * @since 10.0.0 */ -@NullMarked -public class OperatingSystem { - - private String name; - private String version; - private String arch; - private String distribution; - - public OperatingSystem() { - final OperatingSystemResolver resolver = new OperatingSystemResolver(); - resolver.resolve(this); - } - - public String getArch() { - return arch; - } - - public void setArch(final String arch) { - this.arch = arch; - } - - @Nullable - public String getDistribution() { - return distribution; - } - - public void setDistribution(@Nullable final String distribution) { - this.distribution = distribution; - } - - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(final String version) { - this.version = version; - } - - @Override - public String toString() { - return "OperatingSystem{" + "name='" + name + '\'' + ", version='" + version + '\'' + ", arch='" + arch + '\'' - + ", distribution='" + distribution + '\'' + '}'; - } +public record OperatingSystem( + @Nullable String name, + @Nullable String version, + @Nullable String arch, + @Nullable String distribution) { } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystemResolver.java b/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystemResolver.java index b5f361a8..894c82c0 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystemResolver.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/os/OperatingSystemResolver.java @@ -3,23 +3,25 @@ import static java.lang.Double.parseDouble; import static java.lang.Integer.parseInt; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; -import org.jspecify.annotations.NullMarked; +import javasabr.rlib.common.util.StringUtils; +import lombok.CustomLog; +import org.jspecify.annotations.Nullable; /** * Resolves operating system information from system properties and configuration files. * * @since 10.0.0 */ -@NullMarked +@CustomLog public class OperatingSystemResolver { public static final String FILE_PROC_VERSION = "/proc/version"; @@ -36,26 +38,34 @@ public class OperatingSystemResolver { private static final String VERSION = System.getProperty("os.version"); private static final String ARCH = System.getProperty("os.arch"); - private static final Map MAC_OS_VERSION_MAPPING = new HashMap<>(); + private static final Map MAC_OS_VERSION_MAPPING = new HashMap<>(); private static final Map DARWIN_VERSION_MAPPING = new HashMap<>(); private static final List LINUX_VERSION_NAMES = new ArrayList<>(); static { - MAC_OS_VERSION_MAPPING.put(10.0, "Puma"); - MAC_OS_VERSION_MAPPING.put(10.1, "Cheetah"); - MAC_OS_VERSION_MAPPING.put(10.2, "Jaguar"); - MAC_OS_VERSION_MAPPING.put(10.3, "Panther"); - MAC_OS_VERSION_MAPPING.put(10.4, "Tiger"); - MAC_OS_VERSION_MAPPING.put(10.5, "Leopard"); - MAC_OS_VERSION_MAPPING.put(10.6, "Snow Leopard"); - MAC_OS_VERSION_MAPPING.put(10.7, "Snow Lion"); - MAC_OS_VERSION_MAPPING.put(10.8, "Mountain Lion"); - MAC_OS_VERSION_MAPPING.put(10.9, "Mavericks"); - MAC_OS_VERSION_MAPPING.put(10.10, "Yosemite"); - MAC_OS_VERSION_MAPPING.put(10.11, "El Capitan "); - MAC_OS_VERSION_MAPPING.put(10.12, "Sierra"); - MAC_OS_VERSION_MAPPING.put(10.13, "High Sierra"); + MAC_OS_VERSION_MAPPING.put("10.0", "Puma"); + MAC_OS_VERSION_MAPPING.put("10.1", "Cheetah"); + MAC_OS_VERSION_MAPPING.put("10.2", "Jaguar"); + MAC_OS_VERSION_MAPPING.put("10.3", "Panther"); + MAC_OS_VERSION_MAPPING.put("10.4", "Tiger"); + MAC_OS_VERSION_MAPPING.put("10.5", "Leopard"); + MAC_OS_VERSION_MAPPING.put("10.6", "Snow Leopard"); + MAC_OS_VERSION_MAPPING.put("10.7", "Snow Lion"); + MAC_OS_VERSION_MAPPING.put("10.8", "Mountain Lion"); + MAC_OS_VERSION_MAPPING.put("10.9", "Mavericks"); + MAC_OS_VERSION_MAPPING.put("10.10", "Yosemite"); + MAC_OS_VERSION_MAPPING.put("10.11", "El Capitan "); + MAC_OS_VERSION_MAPPING.put("10.12", "Sierra"); + MAC_OS_VERSION_MAPPING.put("10.13", "High Sierra"); + MAC_OS_VERSION_MAPPING.put("10.14", "Mojave"); + MAC_OS_VERSION_MAPPING.put("10.15", "Catalina"); + MAC_OS_VERSION_MAPPING.put("11", "Big Sur"); + MAC_OS_VERSION_MAPPING.put("12", "Monterey"); + MAC_OS_VERSION_MAPPING.put("13", "Ventura"); + MAC_OS_VERSION_MAPPING.put("14", "Sonoma"); + MAC_OS_VERSION_MAPPING.put("15", "Sequoia"); + MAC_OS_VERSION_MAPPING.put("26", "Tahoe"); DARWIN_VERSION_MAPPING.put(5, "Puma"); DARWIN_VERSION_MAPPING.put(6, "Jaguar"); @@ -71,180 +81,158 @@ public class OperatingSystemResolver { LINUX_VERSION_NAMES.addAll(Arrays.asList("Linux", "SunOS")); } - private String findFile(final File dir, final String postfix) { - - final File[] files = dir.listFiles((FilenameFilter) (directory, filename) -> filename.endsWith(postfix)); - - if (files != null && files.length > 0) { - return files[0].getAbsolutePath(); - } - - return null; - } - /** - * Resolve. + * Resolves details of the current operating system. * - * @param system the system + * @return resolved operating system details + * @since 10.0.0 */ - protected void resolve(final OperatingSystem system) { - - system.setName(NAME); - system.setArch(ARCH); - system.setVersion(VERSION); - - // Windows is quite easy to tackle with - if (NAME.startsWith("Windows")) { - system.setDistribution(NAME); - } - // Mac requires a bit of work, but at least it's consistent - else if (NAME.startsWith("Mac")) { - resolveMacOs(system); + public OperatingSystem resolve() { + if (NAME.startsWith("Mac")) { + return new OperatingSystem(NAME, VERSION, ARCH, resolveMacDistribution()); } else if (NAME.startsWith("Darwin")) { - resolveDarwinOs(system); - } - // Try to detect other POSIX compliant platforms, now the fun begins - else { - for (final String name : LINUX_VERSION_NAMES) { + return new OperatingSystem(NAME, VERSION, ARCH, resolveDarwinDistribution()); + } else { + for (String name : LINUX_VERSION_NAMES) { if (NAME.startsWith(name)) { - resolveLinuxOs(system); + return new OperatingSystem(NAME, VERSION, ARCH, resolveLinuxDistribution()); } } } + return new OperatingSystem(NAME, VERSION, ARCH, NAME); } - private void resolveDarwinOs(final OperatingSystem system) { - final String[] versions = VERSION.split("\\."); - system.setDistribution("OS X " + DARWIN_VERSION_MAPPING.get(parseInt(versions[0])) + " (" + VERSION + ")"); + private String resolveDarwinDistribution() { + String[] versions = VERSION.split("\\."); + return "OS X " + DARWIN_VERSION_MAPPING.get(parseInt(versions[0])) + " (" + VERSION + ")"; } - private void resolveLinuxOs(final OperatingSystem system) { - + private String resolveLinuxDistribution() { // The most likely is to have a LSB compliant distro - resolveNameFromLsbRelease(system); - - if (system.getDistribution() != null) { - return; + String distribution = resolveNameFromLsbRelease(); + if (StringUtils.isNotBlank(distribution)) { + return distribution; } - // Generic Linux platform name - resolveNameFromFile(system, FILE_ETC_SYSTEM_RELEASE); - - if (system.getDistribution() != null) { - return; + distribution = resolveNameFromFile(FILE_ETC_SYSTEM_RELEASE); + if (StringUtils.isNotBlank(distribution)) { + return distribution; } - - final File dir = new File(FILE_ETC); - - if (dir.exists()) { - + var etcDirectory = Path.of(FILE_ETC); + if (Files.exists(etcDirectory)) { // if generic 'system-release' file is not present, then try to find // another one - resolveNameFromFile(system, findFile(dir, "-release")); - - if (system.getDistribution() != null) { - return; + distribution = resolveNameFromFile(findFile(etcDirectory, "-release")); + if (StringUtils.isNotBlank(distribution)) { + return distribution; } // if generic 'system-release' file is not present, then try to find // '_version' - resolveNameFromFile(system, findFile(dir, "-_version")); - - if (system.getDistribution() != null) { - return; + distribution = resolveNameFromFile(findFile(etcDirectory, "-_version")); + if (StringUtils.isNotBlank(distribution)) { + return distribution; } // try with /etc/issue file - resolveNameFromFile(system, FILE_ETC_ISSUE); - } - - if (system.getDistribution() != null) { - return; + distribution = resolveNameFromFile(FILE_ETC_ISSUE); + if (StringUtils.isNotBlank(distribution)) { + return distribution; + } } // if nothing found yet, looks for the version info - final File fileVersion = new File(FILE_PROC_VERSION); - - if (fileVersion.exists()) { - resolveNameFromFile(system, fileVersion.getAbsolutePath()); + var fileVersion = Path.of(FILE_PROC_VERSION); + if (Files.exists(fileVersion)) { + distribution = resolveNameFromFile(fileVersion.toString()); + if (StringUtils.isNotBlank(distribution)) { + return distribution; + } } - if (system.getDistribution() != null) { - system.setDistribution(NAME); - } + return NAME; } - private void resolveMacOs(final OperatingSystem system) { - - final String[] versions = VERSION.split("\\."); - - final double version = parseDouble(versions[0] + "." + versions[1]); - - if (version < 10) { - system.setDistribution("Mac OS " + VERSION); - } else { - system.setDistribution("OS X " + MAC_OS_VERSION_MAPPING.get(version) + " (" + VERSION + ")"); + private String resolveMacDistribution() { + String normalizedVersion = normalizeMacVersion(VERSION); + if (normalizedVersion == null) { + return "OS X " + VERSION; + } else if (parseDouble(normalizedVersion) < 10) { + return "Mac OS " + VERSION; } + String distribution = MAC_OS_VERSION_MAPPING.get(normalizedVersion); + return distribution == null ? "OS X " + VERSION : "OS X " + distribution + " (" + VERSION + ")"; } - private void resolveNameFromFile(final OperatingSystem system, final String filename) { - - if (filename == null) { - return; + @Nullable + static String normalizeMacVersion(@Nullable String version) { + if (StringUtils.isBlank(version)) { + return null; } - - final File file = new File(filename); - - if (!file.exists()) { - return; + String[] versions = version.split("\\."); + if (versions.length == 0 || StringUtils.isBlank(versions[0])) { + return null; + } + int majorVersion; + try { + majorVersion = parseInt(versions[0]); + } catch (NumberFormatException e) { + return null; + } + if (majorVersion < 10) { + return version; } + if (majorVersion == 10) { + if (versions.length < 2 || StringUtils.isBlank(versions[1])) { + return null; + } + try { + return majorVersion + "." + parseInt(versions[1]); + } catch (NumberFormatException e) { + return null; + } + } + return String.valueOf(majorVersion); + } + @Nullable + private String resolveNameFromFile(@Nullable String filePath) { + if (filePath == null) { + return null; + } + var file = Path.of(filePath); + if (!Files.exists(file)) { + return null; + } String lastLine = null; - - try (Scanner scanner = new Scanner(file)) { - + try (Scanner scanner = new Scanner(Files.newBufferedReader(file))) { int lineNb = 0; - while (scanner.hasNextLine()) { - - final String line = scanner.nextLine(); - + String line = scanner.nextLine(); if (lineNb++ == 0) { lastLine = line; } - if (line.startsWith(PROP_PRETTY_NAME)) { - system.setDistribution(line.substring(13, line.length() - 1)); - break; + return line.substring(13, line.length() - 1); } } - - } catch (final FileNotFoundException e) { - e.printStackTrace(); - } - - if (system.getDistribution() == null) { - system.setDistribution(lastLine); + } catch (IOException e) { + log.warn(e); } + return lastLine; } - private void resolveNameFromLsbRelease(final OperatingSystem system) { - - final File file = new File(FILE_ETC_LSB_RELEASE); - - if (!file.exists()) { - return; + @Nullable + private String resolveNameFromLsbRelease() { + var lsbReleaseFile = Path.of(FILE_ETC_LSB_RELEASE); + if (!Files.exists(lsbReleaseFile)) { + return null; } - String description = null; String codename = null; - - try (Scanner scanner = new Scanner(file)) { - + try (Scanner scanner = new Scanner(Files.newBufferedReader(lsbReleaseFile))) { while (scanner.hasNextLine()) { - - final String line = scanner.nextLine(); - + String line = scanner.nextLine(); if (line.startsWith(PROP_DISTRIB_DESCRIPTION)) { description = line .replace(PROP_DISTRIB_DESCRIPTION + "=", "") @@ -252,15 +240,27 @@ private void resolveNameFromLsbRelease(final OperatingSystem system) { } else if (line.startsWith(PROP_DISTRIB_CODENAME)) { codename = line.replace(PROP_DISTRIB_CODENAME + "=", ""); } - if (description != null && codename != null) { - system.setDistribution(description + " (" + codename + ")"); - break; + return description + " (" + codename + ")"; } } - - } catch (final FileNotFoundException e) { - e.printStackTrace(); + } catch (IOException e) { + log.warn(e); + } + return null; + } + + @Nullable + private String findFile(Path directory, String postfix) { + try (var stream = Files.list(directory)) { + return stream + .map(Path::toString) + .filter(fileName -> fileName.endsWith(postfix)) + .findFirst() + .orElse(null); + } catch (IOException e) { + log.warn(e); + return null; } } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/os/package-info.java b/rlib-common/src/main/java/javasabr/rlib/common/util/os/package-info.java new file mode 100644 index 00000000..0d8ba755 --- /dev/null +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/os/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.common.util.os; + +import org.jspecify.annotations.NullMarked; diff --git a/rlib-common/src/test/java/javasabr/rlib/common/BaseTest.java b/rlib-common/src/test/java/javasabr/rlib/common/BaseTest.java deleted file mode 100644 index ab03c8e1..00000000 --- a/rlib-common/src/test/java/javasabr/rlib/common/BaseTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package javasabr.rlib.common; - -public class BaseTest { - - protected static class Type1 { - public static final Type1 EXAMPLE = new Type1(); - } - - protected static class Type2 { - public static final Type2 EXAMPLE = new Type2(); - } - - public void assertType(T object, Class type) { - if (!type.isInstance(object)) { - throw new ClassCastException(); - } - } - - public void assertIntType(Integer val) { - } - - public void assertLongType(Long val) { - } - - public void assertFloatType(Float val) { - } -} diff --git a/rlib-common/src/test/java/javasabr/rlib/common/util/ArrayUtilsTest.java b/rlib-common/src/test/java/javasabr/rlib/common/util/ArrayUtilsTest.java index ffc31718..cfad430b 100644 --- a/rlib-common/src/test/java/javasabr/rlib/common/util/ArrayUtilsTest.java +++ b/rlib-common/src/test/java/javasabr/rlib/common/util/ArrayUtilsTest.java @@ -503,7 +503,7 @@ void shouldFindIndexWithCondition() { // given: String[] array = {"apple", "banana", "cherry"}; // when: - int index = ArrayUtils.indexOf(array, "an", (element, arg) -> element.contains(arg)); + int index = ArrayUtils.indexOf(array, "an", String::contains); // then: assertThat(index).isEqualTo(1); } @@ -513,7 +513,7 @@ void shouldFindIndexWithConditionInRange() { // given: String[] array = {"apple", "banana", "avocado", "apricot"}; // when: - int index = ArrayUtils.indexOf(array, "a", (e, arg) -> e.startsWith(arg), 1, 4); + int index = ArrayUtils.indexOf(array, "a", String::startsWith, 1, 4); // then: assertThat(index).isEqualTo(2); } @@ -880,7 +880,7 @@ void shouldReturnNullWhenNoMatch() { @Test void shouldReturnNullForNullArray() { // when: - String result = ArrayUtils.findAny((String[]) null, s -> true); + String result = ArrayUtils.findAny(null, s -> true); // then: assertThat(result).isNull(); } @@ -890,7 +890,7 @@ void shouldFindAnyWithArgument() { // given: String[] array = {"apple", "banana", "cherry"}; // when: - String result = ArrayUtils.findAny(array, "an", (s, arg) -> s.contains(arg)); + String result = ArrayUtils.findAny(array, "an", String::contains); // then: assertThat(result).isEqualTo("banana"); } @@ -900,7 +900,7 @@ void shouldFindAnySubElementWithGetter() { // given: String[] array = {"ab", "abc", "abcd"}; // when: - Integer result = ArrayUtils.findAny(array, 3, String::length, (len, target) -> len.equals(target)); + Integer result = ArrayUtils.findAny(array, 3, String::length, Integer::equals); // then: assertThat(result).isEqualTo(3); } diff --git a/rlib-common/src/test/java/javasabr/rlib/common/util/os/OperatingSystemResolverTest.java b/rlib-common/src/test/java/javasabr/rlib/common/util/os/OperatingSystemResolverTest.java new file mode 100644 index 00000000..e080606d --- /dev/null +++ b/rlib-common/src/test/java/javasabr/rlib/common/util/os/OperatingSystemResolverTest.java @@ -0,0 +1,76 @@ +package javasabr.rlib.common.util.os; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class OperatingSystemResolverTest { + + @Test + void shouldResolveOperatingSystem() { + // given: + var resolver = new OperatingSystemResolver(); + + //when: + OperatingSystem system = resolver.resolve(); + + // then: + Assertions + .assertThat(system) + .isNotNull() + .hasNoNullFieldsOrProperties(); + } + + @Test + void shouldNormalizeMacVersion10WithPatchPart() { + // given: + String version = "10.15.7"; + + // when: + String normalized = OperatingSystemResolver.normalizeMacVersion(version); + + // then: + assertThat(normalized) + .isEqualTo("10.15"); + } + + @Test + void shouldNormalizeMacVersion11PlusToMajor() { + // given: + String version = "15.1"; + + // when: + String normalized = OperatingSystemResolver.normalizeMacVersion(version); + + // then: + assertThat(normalized) + .isEqualTo("15"); + } + + @Test + void shouldKeepLegacyMacVersionAsIs() { + // given: + String version = "9.2.1"; + + // when: + String normalized = OperatingSystemResolver.normalizeMacVersion(version); + + // then: + assertThat(normalized) + .isEqualTo("9.2.1"); + } + + @Test + void shouldReturnNullForInvalidVersion() { + // given: + String version = "unknown"; + + // when: + String normalized = OperatingSystemResolver.normalizeMacVersion(version); + + // then: + assertThat(normalized) + .isNull(); + } +} diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JavaFileSource.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JavaFileSource.java index 4e85f77b..1f502907 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JavaFileSource.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JavaFileSource.java @@ -7,6 +7,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import javax.tools.SimpleJavaFileObject; +import org.jspecify.annotations.Nullable; /** * @author JavaSaBr @@ -22,7 +23,7 @@ protected JavaFileSource(URI uri) { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java index 7f2f0acb..61f346e5 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java @@ -8,8 +8,6 @@ import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.compiler.Compiler; import javasabr.rlib.io.util.FileUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javax.tools.Diagnostic; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; @@ -17,6 +15,7 @@ import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; @@ -26,12 +25,11 @@ * @author JavaSaBr */ @Getter +@CustomLog @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class JdkCompiler implements Compiler { - private static final Logger LOGGER = LoggerManager.getLogger(JdkCompiler.class); - private static final Array EMPTY_OPTIONS = Array.empty(String.class); private static final Class[] EMPTY_CLASSES = new Class[0]; @@ -103,7 +101,7 @@ protected synchronized Array> compile( @Nullable Array options, Array sources) { - LOGGER.debug(sources.size(), "Start compiling [%s] source files..."::formatted); + log.debug(sources.size(), "Start compiling [%s] source files..."::formatted); JavaCompiler compiler = compiler(); CompiledJavaFileManager fileManager = fileManager(); @@ -116,24 +114,24 @@ protected synchronized Array> compile( Array> diagnostics = listener.diagnostics(); if (showDiagnostic() && !diagnostics.isEmpty()) { - LOGGER.warning("Compilation messages:"); + log.warn("Compilation messages:"); for (Diagnostic diagnostic : diagnostics) { - LOGGER.warning(String.valueOf(diagnostic)); + log.warn(String.valueOf(diagnostic)); } } MutableArray> result = MutableArray.ofType(Class.class); String[] classNames = fileManager.classNames(); - LOGGER.debug(classNames.length, "Got [%s] compiled class names"::formatted); + log.debug(classNames.length, "Got [%s] compiled class names"::formatted); for (String className : classNames) { - LOGGER.debug(className, "Try to load class:[%s]"::formatted); + log.debug(className, "Try to load class:[%s]"::formatted); try { Class klass = Class.forName(className, false, loader); result.add(klass); } catch (ClassNotFoundException e) { - LOGGER.warning(e); + log.warn(e); } } diff --git a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadPeriodicTaskExecutor.java b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadPeriodicTaskExecutor.java index be85e367..e675cb9f 100644 --- a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadPeriodicTaskExecutor.java +++ b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadPeriodicTaskExecutor.java @@ -16,9 +16,8 @@ import javasabr.rlib.concurrent.task.PeriodicTask; import javasabr.rlib.concurrent.util.ConcurrentUtils; import javasabr.rlib.functions.LongObjConsumer; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; @@ -26,13 +25,12 @@ /** * @author JavaSaBr */ +@CustomLog @Accessors(fluent = true, chain = false) @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class SingleThreadPeriodicTaskExecutor, L> implements PeriodicTaskExecutor, Runnable, Lockable { - - protected static final Logger LOGGER = LoggerManager.getLogger(SingleThreadPeriodicTaskExecutor.class); - + MutableArray waitTasks; MutableArray executeTasks; MutableArray finishedTasks; @@ -190,7 +188,7 @@ public void run() { try { executeImpl(tasksToExecute, finishedTasks, local, startExecuteTime); } catch (Exception exc) { - LOGGER.warning(exc); + log.warn(exc); } finally { postExecute(tasksToExecute, local, startExecuteTime); } @@ -210,7 +208,7 @@ public void run() { } } catch (Exception exc) { - LOGGER.warning(exc); + log.warn(exc); } if (interval < 1) { diff --git a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadTaskExecutor.java b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadTaskExecutor.java index 03c313d2..bbe2ba00 100644 --- a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadTaskExecutor.java +++ b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/SingleThreadTaskExecutor.java @@ -14,19 +14,17 @@ import javasabr.rlib.concurrent.task.CallableTask; import javasabr.rlib.concurrent.task.SimpleTask; import javasabr.rlib.concurrent.util.ConcurrentUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ +@CustomLog @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class SingleThreadTaskExecutor implements TaskExecutor, Runnable, Lockable { - - protected static final Logger LOGGER = LoggerManager.getLogger(SingleThreadTaskExecutor.class); - + MutableArray> waitTasks; Thread thread; @@ -108,7 +106,7 @@ public void run() { .iterations() .forEach(local, System.currentTimeMillis(), CallableTask::call); } catch (final Exception e) { - LOGGER.warning(e); + log.warn(e); } } } diff --git a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/ThreadPoolTaskExecutor.java b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/ThreadPoolTaskExecutor.java index 311c3913..dc1ca0b0 100644 --- a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/ThreadPoolTaskExecutor.java +++ b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/executor/impl/ThreadPoolTaskExecutor.java @@ -15,19 +15,17 @@ import javasabr.rlib.concurrent.task.CallableTask; import javasabr.rlib.concurrent.task.SimpleTask; import javasabr.rlib.concurrent.util.ConcurrentUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ +@CustomLog @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class ThreadPoolTaskExecutor implements TaskExecutor, Runnable, Lockable { - - protected static final Logger LOGGER = LoggerManager.getLogger(ThreadPoolTaskExecutor.class); - + Deque> waitTasks; MutableArray threads; @@ -114,7 +112,7 @@ public void run() { .iterations() .forEach(local, System.currentTimeMillis(), CallableTask::call); } catch (Exception e) { - LOGGER.warning(e); + log.warn(e); } } } diff --git a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/util/ConcurrentUtils.java b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/util/ConcurrentUtils.java index 06769cbe..2209f89e 100644 --- a/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/util/ConcurrentUtils.java +++ b/rlib-concurrent/src/main/java/javasabr/rlib/concurrent/util/ConcurrentUtils.java @@ -3,19 +3,17 @@ import java.util.function.Function; import javasabr.rlib.concurrent.lock.Lockable; import javasabr.rlib.functions.ObjIntFunction; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; +import lombok.CustomLog; import lombok.experimental.UtilityClass; import org.jspecify.annotations.Nullable; /** * @author JavaSaBr */ +@CustomLog @UtilityClass public final class ConcurrentUtils { - - private static final Logger LOGGER = LoggerManager.getLogger(ConcurrentUtils.class); - + public static void notifyAll(Object object) { synchronized (object) { object.notifyAll(); @@ -38,7 +36,7 @@ public static void wait(Object object) { try { object.wait(); } catch (final InterruptedException e) { - LOGGER.warning(e); + log.warn(e); } } } @@ -48,7 +46,7 @@ public static void wait(Object object, long time) { try { object.wait(time); } catch (final InterruptedException e) { - LOGGER.warning(e); + log.warn(e); } } } @@ -57,7 +55,7 @@ public static void waitInSynchronize(Object object) { try { object.wait(); } catch (final InterruptedException e) { - LOGGER.warning(e); + log.warn(e); } } @@ -65,7 +63,7 @@ public static void waitInSynchronize(Object object, long time) { try { object.wait(time); } catch (final InterruptedException e) { - LOGGER.warning(e); + log.warn(e); } } diff --git a/rlib-fx/src/main/java/javasabr/rlib/fx/handler/ControlResizeHandler.java b/rlib-fx/src/main/java/javasabr/rlib/fx/handler/ControlResizeHandler.java index c9b74b1f..4c78eb26 100644 --- a/rlib-fx/src/main/java/javasabr/rlib/fx/handler/ControlResizeHandler.java +++ b/rlib-fx/src/main/java/javasabr/rlib/fx/handler/ControlResizeHandler.java @@ -51,7 +51,7 @@ public static void install(Region node, int borderWidth) { private double startX; private double startY; - private int border; + private final int border; public ControlResizeHandler(Region node, int borderWidth) { this.node = node; diff --git a/rlib-fx/src/main/java/javasabr/rlib/fx/handler/WindowResizeHandler.java b/rlib-fx/src/main/java/javasabr/rlib/fx/handler/WindowResizeHandler.java index 4e98b54d..467c6a27 100644 --- a/rlib-fx/src/main/java/javasabr/rlib/fx/handler/WindowResizeHandler.java +++ b/rlib-fx/src/main/java/javasabr/rlib/fx/handler/WindowResizeHandler.java @@ -57,7 +57,7 @@ private static void addHandler(Node node, EventHandler listener) { private double startX; private double startY; - private int border; + private final int border; public WindowResizeHandler(Stage stage) { this.stage = stage; diff --git a/rlib-fx/src/main/java/javasabr/rlib/fx/util/ObservableUtils.java b/rlib-fx/src/main/java/javasabr/rlib/fx/util/ObservableUtils.java index 95646acc..5cf3d0e4 100644 --- a/rlib-fx/src/main/java/javasabr/rlib/fx/util/ObservableUtils.java +++ b/rlib-fx/src/main/java/javasabr/rlib/fx/util/ObservableUtils.java @@ -111,7 +111,7 @@ public ChangeEventAppender onChangeIf(BiPredicate predicate, BiConsumer } @Override - public void close() throws Exception { + public void close() { value = null; } } diff --git a/rlib-geometry/src/main/java/javasabr/rlib/geometry/Quaternion4f.java b/rlib-geometry/src/main/java/javasabr/rlib/geometry/Quaternion4f.java index f8e9655e..375115a7 100644 --- a/rlib-geometry/src/main/java/javasabr/rlib/geometry/Quaternion4f.java +++ b/rlib-geometry/src/main/java/javasabr/rlib/geometry/Quaternion4f.java @@ -233,8 +233,7 @@ public Vector3f getDirection(DirectionType type, @Nullable Vector3f store) { } @Override - public final boolean equals(Object obj) { - + public final boolean equals(@Nullable Object obj) { if (this == obj) { return true; } else if (obj == null) { @@ -242,9 +241,7 @@ public final boolean equals(Object obj) { } else if (getClass() != obj.getClass()) { return false; } - Quaternion4f other = (Quaternion4f) obj; - if (Float.floatToIntBits(w) != Float.floatToIntBits(other.w)) { return false; } else if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) { @@ -254,7 +251,6 @@ public final boolean equals(Object obj) { } else if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) { return false; } - return true; } diff --git a/rlib-geometry/src/main/java/javasabr/rlib/geometry/Ray3f.java b/rlib-geometry/src/main/java/javasabr/rlib/geometry/Ray3f.java index 4fdabccd..086e1dfb 100644 --- a/rlib-geometry/src/main/java/javasabr/rlib/geometry/Ray3f.java +++ b/rlib-geometry/src/main/java/javasabr/rlib/geometry/Ray3f.java @@ -1,23 +1,11 @@ package javasabr.rlib.geometry; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import lombok.experimental.FieldDefaults; - /** * Represents a 3D ray with a start point and direction. * * @since 10.0.0 */ -@Getter -@Accessors(fluent = true) -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class Ray3f { - - Vector3f start, direction; +public record Ray3f(Vector3f start, Vector3f direction) { /** * Constructs a ray at the origin with zero direction. @@ -28,11 +16,11 @@ public Ray3f() { this(new Vector3f(), new Vector3f()); } - public final void direction(Vector3f direction) { + public void direction(Vector3f direction) { this.direction.set(direction); } - public final void start(Vector3f start) { + public void start(Vector3f start) { this.start.set(start); } diff --git a/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AbstractBounding.java b/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AbstractBounding.java index 401f8806..328114b1 100644 --- a/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AbstractBounding.java +++ b/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AbstractBounding.java @@ -6,9 +6,8 @@ import javasabr.rlib.geometry.Vector3fBuffer; import javasabr.rlib.geometry.bounding.Bounding; import javasabr.rlib.geometry.bounding.BoundingType; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; @@ -17,12 +16,11 @@ * @author JavaSaBr */ @Getter +@CustomLog @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public abstract class AbstractBounding implements Bounding { - - protected static final Logger LOGGER = LoggerManager.getLogger(Bounding.class); - + Vector3f center, offset; protected AbstractBounding(Vector3f center, Vector3f offset) { diff --git a/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AxisAlignedBoundingBox.java b/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AxisAlignedBoundingBox.java index 61ea07c4..8523c567 100644 --- a/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AxisAlignedBoundingBox.java +++ b/rlib-geometry/src/main/java/javasabr/rlib/geometry/bounding/impl/AxisAlignedBoundingBox.java @@ -7,6 +7,7 @@ import javasabr.rlib.geometry.bounding.Bounding; import javasabr.rlib.geometry.bounding.BoundingType; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; @@ -15,6 +16,7 @@ * @author JavaSaBr */ @Getter +@CustomLog @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PROTECTED) public class AxisAlignedBoundingBox extends AbstractBounding { @@ -126,7 +128,7 @@ public boolean intersects(Bounding bounding, Vector3fBuffer buffer) { } default: { - LOGGER.warning(new IllegalArgumentException("incorrect bounding type " + bounding.boundingType())); + log.warn(new IllegalArgumentException("incorrect bounding type " + bounding.boundingType())); } } diff --git a/rlib-geometry/src/main/java/javasabr/rlib/geometry/util/CoordsUtils.java b/rlib-geometry/src/main/java/javasabr/rlib/geometry/util/CoordsUtils.java index 47f86bef..046ed6c4 100644 --- a/rlib-geometry/src/main/java/javasabr/rlib/geometry/util/CoordsUtils.java +++ b/rlib-geometry/src/main/java/javasabr/rlib/geometry/util/CoordsUtils.java @@ -74,7 +74,6 @@ public static float calcY(float y, int distance, int heading, int offset) { return y + distance * (float) Math.sin(AngleUtils.headingToRadians(heading + offset)); } - @SuppressWarnings("unchecked") public static Vector3f[] circularCoords(float x, float y, float z, int radius, int count) { Vector3f[] locs = new Vector3f[count]; diff --git a/rlib-io/src/main/java/javasabr/rlib/io/impl/RedirectImageOutputStream.java b/rlib-io/src/main/java/javasabr/rlib/io/impl/RedirectImageOutputStream.java index 94e693a3..eb5e82e8 100644 --- a/rlib-io/src/main/java/javasabr/rlib/io/impl/RedirectImageOutputStream.java +++ b/rlib-io/src/main/java/javasabr/rlib/io/impl/RedirectImageOutputStream.java @@ -22,7 +22,7 @@ public class RedirectImageOutputStream extends ImageOutputStreamImpl { @Nullable InputStream in; @Override - public void close() throws IOException {} + public void close() {} @Override public int read() throws IOException { diff --git a/rlib-io/src/main/java/javasabr/rlib/io/impl/ReuseBytesInputStream.java b/rlib-io/src/main/java/javasabr/rlib/io/impl/ReuseBytesInputStream.java index 55cfa96f..49a13750 100644 --- a/rlib-io/src/main/java/javasabr/rlib/io/impl/ReuseBytesInputStream.java +++ b/rlib-io/src/main/java/javasabr/rlib/io/impl/ReuseBytesInputStream.java @@ -20,7 +20,7 @@ public ReuseBytesInputStream() { this.buffer = ArrayUtils.EMPTY_BYTE_ARRAY; } - public ReuseBytesInputStream(byte buffer[]) { + public ReuseBytesInputStream(byte[] buffer) { this.buffer = buffer; this.position = 0; this.count = buffer.length; diff --git a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java index 2c249f9d..c741f252 100644 --- a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java +++ b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java @@ -34,6 +34,7 @@ import javasabr.rlib.common.util.Utils; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; +import lombok.CustomLog; import org.jspecify.annotations.Nullable; /** @@ -42,6 +43,7 @@ * * @since 10.0.0 */ +@CustomLog public class FileUtils { private static final Logger LOGGER = LoggerManager.getLogger(FileUtils.class); @@ -181,9 +183,7 @@ public static Path[] getFiles(Package pckg, String @Nullable ... extensions) { .getName() .replace('.', '/')); } catch (IOException exc) { - LoggerManager - .getDefaultLogger() - .warning(exc); + Utils.printWarn(exc); } if (urls == null) { @@ -198,7 +198,7 @@ public static Path[] getFiles(Package pckg, String @Nullable ... extensions) { var path = next.getFile(); if (path.contains("%20")) { - path = path.replaceAll("%20", " "); + path = path.replace("%20", " "); } var file = Paths.get(path); @@ -233,9 +233,7 @@ public static void collectFilesTo( } if (!Files.exists(directory)) { - LoggerManager - .getDefaultLogger() - .warning(directory, "Directory:[%s] not found"::formatted); + Utils.printWarn("Directory:[%s] not found".formatted(directory)); return; } @@ -493,16 +491,13 @@ public static String getExtension(Path file, boolean toLowerCase) { */ @Nullable public static String getNameWithoutExtension(@Nullable String fileName) { - if (StringUtils.isEmpty(fileName)) { return fileName; } - int index = fileName.lastIndexOf('.'); if (index == -1) { return fileName; } - return fileName.substring(0, index); } @@ -599,7 +594,7 @@ public static int unzip(Path destination, Path zipFile) { .resolve(entryName) .normalize(); if (!targetFile.startsWith(normalizedDestination)) { - LOGGER.warning(entryName, "Unexpected entry name:[%s] which is outside"::formatted); + LOGGER.warn(entryName, "Unexpected entry name:[%s] which is outside"::formatted); continue; } if (entry.isDirectory()) { diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java index 70244492..5fe3ecd8 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java @@ -105,7 +105,7 @@ interface N1Int2Factory { * Factory for creating log messages with an object, an int, and another object argument. * * @param the type of the first object argument - * @param the type of the second object argument + * @param the type of the third object argument * @since 10.0.0 */ @FunctionalInterface @@ -115,6 +115,20 @@ interface N1IntN1Factory { String make(A arg1, int arg2, C arg3); } + /** + * Factory for creating log messages with two objects and one int argument. + * + * @param the type of the first object argument + * @param the type of the second object argument + * @since 10.0.0 + */ + @FunctionalInterface + interface N2IntFactory { + + @NonNull + String make(A arg1, B arg2, int arg3); + } + /** * Factory for creating log messages with two int arguments. * @@ -223,6 +237,15 @@ default void debug(A arg1, B arg2, C arg3, @NonNull N3Factory print(LoggerLevel.DEBUG, arg1, arg2, arg3, factory); } + /** + * Prints a debug message produced by the provided factory. + * + * @since 10.0.0 + */ + default void debug(A arg1, B arg2, int arg3, @NonNull N2IntFactory factory) { + print(LoggerLevel.DEBUG, arg1, arg2, arg3, factory); + } + default void debug(A arg1, B arg2, C arg3, D arg4, @NonNull N4Factory factory) { print(LoggerLevel.DEBUG, arg1, arg2, arg3, arg4, factory); } @@ -366,50 +389,186 @@ default void overrideEnabled(@NonNull LoggerLevel level, boolean enabled) {} */ default void resetToDefault(@NonNull LoggerLevel level) {} + /** + * Prints a warning message. + * + * @param message the message to print + * @since 10.0.0 + */ + default void warn(@NonNull String message) { + print(LoggerLevel.WARNING, message); + } + + @Deprecated(forRemoval = true) default void warning(@NonNull String message) { print(LoggerLevel.WARNING, message); } + /** + * Prints a warning exception. + * + * @param exception the exception to print + * @since 10.0.0 + */ + default void warn(@NonNull Throwable exception) { + print(LoggerLevel.WARNING, exception); + } + + @Deprecated(forRemoval = true) default void warning(@NonNull Throwable exception) { print(LoggerLevel.WARNING, exception); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, @NonNull N1Factory factory) { + print(LoggerLevel.WARNING, arg1, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, @NonNull N1Factory factory) { print(LoggerLevel.WARNING, arg1, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(int arg1, @NonNull IntFactory factory) { + print(LoggerLevel.WARNING, arg1, factory); + } + + @Deprecated(forRemoval = true) default void warning(int arg1, @NonNull IntFactory factory) { print(LoggerLevel.WARNING, arg1, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, B arg2, @NonNull N2Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, B arg2, @NonNull N2Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(int arg1, int arg2, @NonNull Int2Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, factory); + } + + @Deprecated(forRemoval = true) default void warning(int arg1, int arg2, @NonNull Int2Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(int arg1, B arg2, @NonNull IntN1Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, factory); + } + + @Deprecated(forRemoval = true) default void warning(int arg1, B arg2, @NonNull IntN1Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, int arg2, @NonNull N1IntFactory factory) { + print(LoggerLevel.WARNING, arg1, arg2, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, int arg2, @NonNull N1IntFactory factory) { print(LoggerLevel.WARNING, arg1, arg2, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, B arg2, C arg3, @NonNull N3Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, B arg2, C arg3, @NonNull N3Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, B arg2, int arg3, @NonNull N2IntFactory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); + } + + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, int arg2, C arg3, @NonNull N1IntN1Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, int arg2, C arg3, @NonNull N1IntN1Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn(A arg1, int arg2, int arg3, @NonNull N1Int2Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); + } + + @Deprecated(forRemoval = true) default void warning(A arg1, int arg2, int arg3, @NonNull N1Int2Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); } + /** + * Prints a warning message produced by the provided factory. + * + * @since 10.0.0 + */ + default void warn( + A arg1, + B arg2, + C arg3, + D arg4, + @NonNull N4Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, arg4, factory); + } + + @Deprecated(forRemoval = true) default void warning( A arg1, B arg2, @@ -486,6 +645,22 @@ default void print( } } + /** + * Prints a level-specific message produced by the provided factory. + * + * @since 10.0.0 + */ + default void print( + @NonNull LoggerLevel level, + A arg1, + B arg2, + int arg3, + @NonNull N2IntFactory factory) { + if (enabled(level)) { + print(level, factory.make(arg1, arg2, arg3)); + } + } + default void print( @NonNull LoggerLevel level, A arg1, diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java index 916aaf07..3eff7a00 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java @@ -16,9 +16,9 @@ @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum LoggerLevel { - INFO("INFO", " ", false, true), + INFO("INFO", " ", true, true), DEBUG("DEBUG", " ", false, false), - WARNING("WARNING", "", true, true), + WARNING("WARN", "", true, true), ERROR("ERROR", " ", true, true); /** diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java index 3cb1dc00..86b0c745 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java @@ -38,7 +38,9 @@ public class LoggerManager { } if (implementation == null) { - System.err.printf("ERROR: No any exist implementation of [%s], will be used null logger%n", LoggerFactory.class); + System.err.printf( + "ERROR: No any exist implementation of [%s], will be used null logger%n", + LoggerFactory.class); LOGGER_FACTORY = new NullLoggerFactory(); } else { try { diff --git a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java index f6e07e7d..044e4ba4 100644 --- a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java +++ b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java @@ -26,7 +26,8 @@ void shouldPrintAllDebugMethods() { "DEBUG_msg8[str5, str6, str7]", "DEBUG_msg9[str8, 54, str9]", "DEBUG_msg10[str10, 37, 76]", - "DEBUG_msg11[str11, str12, str13, str14]"); + "DEBUG_msg11[str11, str12, str13, str14]", + "DEBUG_msg12[str15, str16, 32]"); Logger logger = new Logger() { @@ -57,9 +58,10 @@ public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { logger.debug("str8", 54, "str9", "msg9[%s, %d, %s]"::formatted); logger.debug("str10", 37, 76, "msg10[%s, %d, %d]"::formatted); logger.debug("str11", "str12", "str13", "str14", "msg11[%s, %s, %s, %s]"::formatted); + logger.debug("str15", "str16", 32, "msg12[%s, %s, %d]"::formatted); // then: - assertThat(messages.size()).isEqualTo(11); + assertThat(messages.size()).isEqualTo(12); assertThat(messages).isEqualTo(expectedMessages); } @@ -122,17 +124,18 @@ void shouldPrintAllWarningMethods() { // given: var messages = new ArrayList(); var expectedMessages = List.of( - "WARNING_msg1", - "WARNING_msg2[str1]", - "WARNING_msg3[13]", - "WARNING_msg4[str2, 15]", - "WARNING_msg5[14, str3]", - "WARNING_msg6[22, 32]", - "WARNING_msg7[str4, str5]", - "WARNING_msg8[str5, str6, str7]", - "WARNING_msg9[str8, 54, str9]", - "WARNING_msg10[str10, 37, 76]", - "WARNING_msg11[str11, str12, str13, str14]"); + "WARN_msg1", + "WARN_msg2[str1]", + "WARN_msg3[13]", + "WARN_msg4[str2, 15]", + "WARN_msg5[14, str3]", + "WARN_msg6[22, 32]", + "WARN_msg7[str4, str5]", + "WARN_msg8[str5, str6, str7]", + "WARN_msg9[str8, 54, str9]", + "WARN_msg10[str10, 37, 76]", + "WARN_msg11[str11, str12, str13, str14]", + "WARN_msg12[str15, str16, 22]"); Logger logger = new Logger() { @@ -152,20 +155,21 @@ public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { }; // when: - logger.warning("msg1"); - logger.warning("str1", "msg2[%s]"::formatted); - logger.warning(13, "msg3[%d]"::formatted); - logger.warning("str2", 15, "msg4[%s, %d]"::formatted); - logger.warning(14, "str3", "msg5[%d, %s]"::formatted); - logger.warning(22, 32, "msg6[%d, %d]"::formatted); - logger.warning("str4", "str5", "msg7[%s, %s]"::formatted); - logger.warning("str5", "str6", "str7", "msg8[%s, %s, %s]"::formatted); - logger.warning("str8", 54, "str9", "msg9[%s, %d, %s]"::formatted); - logger.warning("str10", 37, 76, "msg10[%s, %d, %d]"::formatted); - logger.warning("str11", "str12", "str13", "str14", "msg11[%s, %s, %s, %s]"::formatted); + logger.warn("msg1"); + logger.warn("str1", "msg2[%s]"::formatted); + logger.warn(13, "msg3[%d]"::formatted); + logger.warn("str2", 15, "msg4[%s, %d]"::formatted); + logger.warn(14, "str3", "msg5[%d, %s]"::formatted); + logger.warn(22, 32, "msg6[%d, %d]"::formatted); + logger.warn("str4", "str5", "msg7[%s, %s]"::formatted); + logger.warn("str5", "str6", "str7", "msg8[%s, %s, %s]"::formatted); + logger.warn("str8", 54, "str9", "msg9[%s, %d, %s]"::formatted); + logger.warn("str10", 37, 76, "msg10[%s, %d, %d]"::formatted); + logger.warn("str11", "str12", "str13", "str14", "msg11[%s, %s, %s, %s]"::formatted); + logger.warn("str15", "str16", 22, "msg12[%s, %s, %d]"::formatted); // then: - assertThat(messages.size()).isEqualTo(11); + assertThat(messages.size()).isEqualTo(12); assertThat(messages).isEqualTo(expectedMessages); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index 6adb0336..50b7115d 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.ArrayIterationFunctions; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerFactory; @@ -31,7 +32,9 @@ public class DefaultLoggerService implements LoggerFactory, LoggerService { ConcurrentMap loggers; MutableArray listeners; + ArrayIterationFunctions listenerIterations; MutableArray writers; + ArrayIterationFunctions writerIterations; Logger logger; DateTimeFormatter timeFormatter; @@ -42,7 +45,9 @@ public DefaultLoggerService() { this.logger = new DefaultLogger("", this); this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); this.listeners = ArrayFactory.copyOnModifyArray(LoggerListener.class); + this.listenerIterations = listeners.iterations(); this.writers = ArrayFactory.copyOnModifyArray(Writer.class); + this.writerIterations = writers.iterations(); this.override = new int[LOGGER_LEVELS.length]; Arrays.fill(override, NOT_CONFIGURE); } @@ -114,31 +119,25 @@ public int enabled(LoggerLevel level) { @Override public void write(LoggerLevel level, String loggerName, String logMessage) { - var timestamp = timeFormatter.format(LocalDateTime.now()); - var resultMessage = level.title() + level.offset() + ' ' + timestamp + ' ' + loggerName + ": " + logMessage; - + var resultMessage = level.title() + + level.offset() + ' ' + + timestamp + ' ' + + loggerName + ": " + + logMessage; write(level, resultMessage); } private void write(LoggerLevel level, String resultMessage) { - - listeners - .iterations() - .forEach(resultMessage, LoggerListener::println); - writers - .iterations() - .forEach(resultMessage, DefaultLoggerService::append); - + listenerIterations.forEach(resultMessage, LoggerListener::println); + writerIterations.forEach(resultMessage, DefaultLoggerService::append); switch (level) { case INFO, DEBUG -> System.out.println(resultMessage); case ERROR, WARNING -> System.err.println(resultMessage); } - if (!level.forceFlush()) { return; } - listeners.forEach(LoggerListener::flush); writers.forEach(DefaultLoggerService::flush); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java index 61eb0aad..fecd0269 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java @@ -11,6 +11,7 @@ import javasabr.rlib.logger.api.LoggerListener; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; /** * @author JavaSaBr @@ -21,14 +22,14 @@ public class FolderFileListener implements LoggerListener { private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyy-MM-dd_HH-mm-ss"); final Path folder; - Writer writer; + + @Nullable + volatile Writer writer; public FolderFileListener(Path folder) { - if (!Files.isDirectory(folder)) { throw new IllegalArgumentException("File:[%s] is not directory".formatted(folder)); } - if (!Files.exists(folder)) { try { Files.createDirectories(folder); @@ -36,19 +37,24 @@ public FolderFileListener(Path folder) { throw new UncheckedIOException(e); } } - this.folder = folder; } public Writer getOrCreateWriter() throws IOException { - - if (writer == null) { - var dateTime = LocalDateTime.now(); - var filename = TIME_FORMATTER.format(dateTime) + ".log"; - writer = Files.newBufferedWriter(folder.resolve(filename), StandardCharsets.UTF_8); + var local = writer; + if (local == null) { + synchronized (this) { + local = writer; + if (local == null) { + var dateTime = LocalDateTime.now(); + var filename = TIME_FORMATTER.format(dateTime) + ".log"; + local = Files.newBufferedWriter(folder.resolve(filename), StandardCharsets.UTF_8); + this.writer = local; + return local; + } + } } - - return writer; + return local; } @Override diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java index f0f00ec3..de57ee36 100644 --- a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java @@ -16,8 +16,6 @@ import java.util.concurrent.atomic.LongAccumulator; import javasabr.rlib.common.util.StringUtils; import javasabr.rlib.common.util.ThreadUtils; -import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.ServerNetworkConfig.SimpleServerNetworkConfig; import javasabr.rlib.network.client.ClientNetwork; import javasabr.rlib.network.impl.DefaultBufferAllocator; @@ -121,7 +119,6 @@ private static class StatisticsCollector { @Test @SneakyThrows void testServerWithMultiplyClients() { - LoggerManager.enable(StringSslNetworkLoadTest.class, LoggerLevel.INFO); //LoggerManager.enable(AbstractSslNetworkPacketReader.class, LoggerLevel.DEBUG); //LoggerManager.enable(AbstractSslNetworkPacketWriter.class, LoggerLevel.DEBUG); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java index c0f4f97c..88f91dab 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java @@ -19,7 +19,7 @@ public abstract class AbstractNetworkPacket> implements * Handles packet data exception. */ protected void handleException(C connection, ByteBuffer buffer, Exception exception) { - log.warning(exception); + log.warn(exception); if (!log.warningEnabled()) { return; } @@ -34,7 +34,7 @@ protected void handleException(C connection, ByteBuffer buffer, Exception except hexDump = hexDump(buffer.array(), buffer.position(), buffer.limit()); } - log.warning(connection.remoteAddress(), name(), buffer, hexDump, + log.warn(connection.remoteAddress(), name(), buffer, hexDump, "[%s] Hexdump for:[%s] -> buffer:[%s]\n[%s]"::formatted); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java index ee683870..b5639ee4 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java @@ -98,7 +98,7 @@ protected String remoteAddress() { @Override public void startRead() { if (connection.closed()) { - log.warning(connection.remoteAddress(), "[%s] Connection is already closed"::formatted); + log.warn(connection.remoteAddress(), "[%s] Connection is already closed"::formatted); return; } else if (!reading.compareAndSet(false, true)) { log.debug(connection.remoteAddress(), "[%s] Connection is already waiting for new data from channel"::formatted); @@ -298,7 +298,7 @@ else if (packetFullLength > tempBigBuffer.capacity()) { log.debug(remoteAddress, readablePacket, "[%s] Finished reading data for packet:[%s]"::formatted); readPackets++; } else { - log.warning(remoteAddress, "[%s] Cannot create any instance of packet to read data"::formatted); + log.warn(remoteAddress, "[%s] Cannot create any instance of packet to read data"::formatted); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java index 84f40ee5..ff3adb61 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java @@ -49,7 +49,7 @@ public AbstractReusableWritableNetworkPacket() { public boolean write(C connection, ByteBuffer buffer) { if (counter.get() < 1) { - log.warning(this, arg -> + log.warn(this, arg -> "Attempt to write is already finished packet:[%s] on thread:[%s]".formatted(arg, Thread.currentThread().getName())); return false; } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java index 7a7f193f..18049857 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java @@ -91,7 +91,7 @@ public IdBasedReadableNetworkPacketRegistry register(Class[] for (int i = 0; i < length; i++) { Class cs = classes[i]; if (!type.isAssignableFrom(cs)) { - log.warning(cs, type, "Found incompatibility packet's type:[%s] with type:[%s]"::formatted); + log.warn(cs, type, "Found incompatibility packet's type:[%s] with type:[%s]"::formatted); continue; } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java index 4f382367..baa1011e 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java @@ -64,7 +64,7 @@ public void completed(AsynchronousSocketChannel channel, DefaultServerNetwork @Override public void failed(Throwable exc, DefaultServerNetwork network) { if (exc instanceof AsynchronousCloseException) { - log.warning("Server network was closed"); + log.warn("Server network was closed"); } else { log.error("Got exception during accepting new connection:"); log.error(exc); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java b/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java index b980883f..919b4e97 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java @@ -231,7 +231,7 @@ public static String hexDump(byte[] array, int offset, int length) { if (i == end) { chars[count] = ch; hexDigit(builder, val) - .append(" ".repeat(15 - count)) + .repeat(" ", 15 - count) .append(" "); if (count < 9) { builder.append(" "); diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/extension/ExtensionPointManager.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/extension/ExtensionPointManager.java index 22a61811..60a1143f 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/extension/ExtensionPointManager.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/extension/ExtensionPointManager.java @@ -4,9 +4,8 @@ import javasabr.rlib.collections.dictionary.DictionaryFactory; import javasabr.rlib.collections.dictionary.LockableRefToRefDictionary; import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.experimental.FieldDefaults; /** @@ -14,11 +13,10 @@ * * @since 10.0.0 */ +@CustomLog @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class ExtensionPointManager { - - private static final Logger LOGGER = LoggerManager.getLogger(ExtensionPointManager.class); - + private static final ExtensionPointManager INSTANCE = new ExtensionPointManager(); /** @@ -67,7 +65,7 @@ public ExtensionPoint create(String id) { try { ExtensionPoint exists = extensionPoints.get(id); if (exists != null) { - LOGGER.warning(id, "Extension point:[%s] is already registered"::formatted); + log.warn(id, "Extension point:[%s] is already registered"::formatted); return ClassUtils.unsafeNNCast(exists); } var extensionPoint = new ExtensionPoint(); @@ -116,7 +114,8 @@ public ExtensionPointManager addExtension(String id, T extension) { * @return this manager * @since 10.0.0 */ - public ExtensionPointManager addExtension(String id, T... extensions) { + @SafeVarargs + public final ExtensionPointManager addExtension(String id, T... extensions) { getOrCreateExtensionPoint(id).register(extensions); return this; } diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java index f22f125f..844dbfb2 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java @@ -30,17 +30,15 @@ import javasabr.rlib.common.util.ClassUtils; import javasabr.rlib.common.util.Utils; import javasabr.rlib.io.util.FileUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.plugin.system.ConfigurablePluginSystem; import javasabr.rlib.plugin.system.Plugin; import javasabr.rlib.plugin.system.PluginContainer; -import javasabr.rlib.plugin.system.PluginSystem; import javasabr.rlib.plugin.system.Version; import javasabr.rlib.plugin.system.annotation.PluginDescription; import javasabr.rlib.plugin.system.exception.InitializePluginException; import javasabr.rlib.plugin.system.exception.PluginException; import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; @@ -49,12 +47,11 @@ /** * @author JavaSaBr */ +@CustomLog @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PROTECTED) public class BasePluginSystem implements ConfigurablePluginSystem { - protected static final Logger LOGGER = LoggerManager.getLogger(PluginSystem.class); - protected record State( Array containers, Array plugins, @@ -113,7 +110,7 @@ public CompletionStage preLoad(Executor executor) { } protected BasePluginSystem preLoadImpl(Executor executor) { - LOGGER.debug("Start pre-loading all plugins..."); + log.debug("Start pre-loading all plugins..."); State current = state.get(); Array>> futures = Array.optionals( @@ -139,8 +136,8 @@ protected BasePluginSystem preLoadImpl(Executor executor) { RefToRefDictionary.empty()); if (state.compareAndSet(current, newState)) { - LOGGER.debug(containers, "Pre-loaded:%s"::formatted); - LOGGER.debug("All plugins were pre-loaded"); + log.debug(containers, "Pre-loaded:%s"::formatted); + log.debug("All plugins were pre-loaded"); return this; } @@ -163,7 +160,7 @@ public CompletionStage initialize(Executor executor) { } protected BasePluginSystem initializeImpl(Executor executor) { - LOGGER.debug("Start loading all plugins..."); + log.debug("Start loading all plugins..."); var current = state.get(); var plugins = current.containers @@ -181,7 +178,7 @@ protected BasePluginSystem initializeImpl(Executor executor) { plugins); if (state.compareAndSet(current, newState)) { - LOGGER.debug("All plugins were initialized"); + log.debug("All plugins were initialized"); return this; } @@ -195,7 +192,7 @@ protected CompletionStage createPluginClass(PluginContainer container, E protected Plugin createPluginClassImpl(PluginContainer container) { var pluginClass = container.pluginClass(); - LOGGER.debug(pluginClass, "Start creating plugin:[%s]"::formatted); + log.debug(pluginClass, "Start creating plugin:[%s]"::formatted); Constructor constructor = ClassUtils.tryGetConstructor(pluginClass, PluginContainer.class); if (constructor == null) { @@ -244,7 +241,7 @@ protected CompletionStage> loadPlugins( throw new UncheckedIOException(e); } - LOGGER.debug(realPath, "Try to pre-load plugins from the folder:[%s]"::formatted); + log.debug(realPath, "Try to pre-load plugins from the folder:[%s]"::formatted); return supplyAsync( () -> FileUtils .stream(realPath) @@ -298,18 +295,18 @@ protected PluginContainer loadPluginImpl( try { scanner.scan(); } catch (Throwable throwable) { - LOGGER.warning(throwable); + log.warn(throwable); return null; } Array> pluginImplementations = scanner.findImplementations(Plugin.class); if (pluginImplementations.isEmpty()) { - LOGGER.warning( + log.warn( directory, "Can't load plugin from directory:[%s] because can't find any implementation of plugin interface"::formatted); return null; } else if (pluginImplementations.size() > 1) { - LOGGER.warning( + log.warn( directory, "Can't load plugin from directory:[%s] because found more than 1 implementation of plugin interface"::formatted); return null; @@ -318,7 +315,7 @@ protected PluginContainer loadPluginImpl( Class pluginClass = notNull(pluginImplementations.first()); PluginDescription description = pluginClass.getAnnotation(PluginDescription.class); if (description == null) { - LOGGER.warning( + log.warn( directory, pluginClass, "Can't load plugin from directory:[%s] because can't find description on class:[%s]"::formatted); @@ -329,7 +326,7 @@ protected PluginContainer loadPluginImpl( if (appVersion != null) { var minVersion = new Version(description.minAppVersion()); if (minVersion.compareTo(appVersion) > 0) { - LOGGER.warning( + log.warn( description.id(), description.minAppVersion(), "Can't load plugin:[%s] because it requires minimum app version:[%s]"::formatted); @@ -406,7 +403,7 @@ public Plugin installPlugin(Path file, boolean needInitialize) { } State current = state.get(); - String folderName = FileUtils.getNameWithoutExtension(file); + String folderName = Objects.requireNonNull(FileUtils.getNameWithoutExtension(file)); Path pluginFolder = installPath.resolve(folderName); if (Files.exists(pluginFolder)) { @@ -430,7 +427,7 @@ public Plugin installPlugin(Path file, boolean needInitialize) { } Class pluginClass = container.pluginClass(); - Constructor constructor = ClassUtils.tryGetConstructor(pluginClass, PluginContainer.class); + Constructor constructor = ClassUtils.tryGetConstructor(pluginClass, PluginContainer.class); if (constructor == null) { throw new InitializePluginException( "Not found base constructor in class:[%s]".formatted(pluginClass), @@ -476,7 +473,7 @@ public Plugin installPlugin(Path file, boolean needInitialize) { return plugin; } - LOGGER.warning("Detected concurrent attempt to install plugin:[%s], trying again...".formatted(pluginClass)); + log.warn("Detected concurrent attempt to install plugin:[%s], trying again...".formatted(pluginClass)); return installPlugin(file, needInitialize); } @@ -487,7 +484,7 @@ public boolean removePlugin(Plugin plugin) { String pluginId = plugin.id(); PluginContainer pluginContainer = current.idToContainer.get(pluginId); if (pluginContainer == null) { - LOGGER.warning("Plugin:[%s] is already removed".formatted(plugin.name())); + log.warn("Plugin:[%s] is already removed".formatted(plugin.name())); return false; } @@ -510,7 +507,7 @@ public boolean removePlugin(Plugin plugin) { return true; } - LOGGER.warning("Detected concurrent attempt to remove plugin:[%s], trying again...".formatted(plugin.name())); + log.warn("Detected concurrent attempt to remove plugin:[%s], trying again...".formatted(plugin.name())); return removePlugin(plugin); } }