From a28a950aa4a328f4a52c8c349342f9ced13f0293 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 12 Aug 2026 15:13:29 +0000 Subject: [PATCH] feat: modernize codebase using Java switch expressions and List#getFirst/getLast APIs (#6334) # Description of Changes This PR introduces a broad modernization of the codebase by adopting newer Java language features and improving code readability and maintainability. ## What was changed - Replaced traditional `switch` statements with modern switch expressions (`case ->`) across multiple classes. - Replaced usages of `List#get(0)` and `List#get(size - 1)` with `getFirst()` and `getLast()` respectively. - Simplified conditional logic using pattern matching (e.g., `instanceof` and switch pattern matching). - Refactored various utility and controller classes to reduce boilerplate and improve clarity. - Removed unused or redundant code (e.g., `parseClientFileIds` method in `MergeController`). - Improved type safety (e.g., using `Class::isInstance` instead of `instanceof` checks in streams). - Cleaned up Spring annotations by removing unnecessary `@Autowired` where constructor injection is already used. - Added a new test (`UIDataControllerTest`) to ensure correct handling of identical JSON configs with different filenames. - Minor formatting and style fixes (e.g., Spotless formatting adjustment). ## Why the change was made - To align the codebase with modern Java standards (Java 17+ features). - To improve readability and maintainability by reducing verbosity. - To eliminate common indexing patterns that are more error-prone. - To standardize coding style across the project. - To improve test coverage for edge cases discovered during refactoring. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Signed-off-by: Ludy87 --- .../configuration/RuntimePathConfig.java | 2 +- .../common/model/oauth2/Provider.java | 44 ++-- .../common/pdf/PdfMarkdownConverter.java | 26 +-- .../common/service/InternalApiClient.java | 2 +- .../software/common/util/ChecksumUtils.java | 33 +-- .../software/common/util/ExceptionUtils.java | 4 +- .../common/util/FormFieldTypeSupport.java | 2 +- .../software/common/util/FormUtils.java | 190 ++++++++---------- .../common/util/ImageProcessingUtils.java | 24 +-- .../software/common/util/PDFToFile.java | 2 +- .../software/common/util/ProcessExecutor.java | 6 +- .../software/common/util/YamlHelper.java | 2 +- .../software/common/util/PDFToFileTest.java | 2 +- .../SPDF/config/MultipartConfiguration.java | 7 +- .../api/BookletImpositionController.java | 15 +- .../SPDF/controller/api/MergeController.java | 27 --- .../SPDF/controller/api/UIDataController.java | 31 +-- .../converters/ConvertImgPDFController.java | 2 +- .../api/converters/ConvertSvgToPDF.java | 6 +- .../api/converters/ExtractCSVController.java | 2 +- .../api/misc/AutoRenameController.java | 4 +- .../api/misc/AutoSplitPdfController.java | 2 +- .../api/misc/CompressController.java | 4 +- .../api/misc/ExtractImageScansController.java | 2 +- .../api/security/CertSignController.java | 2 +- .../api/security/RedactExecuteService.java | 2 +- .../security/RemoveCertSignController.java | 2 +- .../controller/web/UploadLimitService.java | 7 +- .../service/PdfJsonConversionService.java | 6 +- .../service/pdfjson/PdfJsonImageService.java | 2 +- .../common/controller/JobController.java | 2 +- .../config/MultipartConfigurationTest.java | 7 +- .../api/MergeControllerGapTest.java | 58 ------ .../controller/api/UIDataControllerTest.java | 66 ++++++ .../ConvertOfficeControllerTest.java | 10 +- .../converters/ConvertPDFToPDFAMoreTest.java | 2 +- .../ConvertPdfToVideoControllerTest.java | 4 +- .../api/misc/CompressControllerMoreTest.java | 2 +- .../api/misc/RemoveImagesControllerTest.java | 4 +- .../api/misc/RepairControllerMoreTest.java | 16 +- .../web/UploadLimitServiceTest.java | 10 +- .../controller/api/AuditRestController.java | 40 ++-- .../policy/engine/PolicyExecutor.java | 2 +- .../security/configuration/CacheConfig.java | 2 - ...mSaml2ResponseAuthenticationConverter.java | 4 +- .../security/service/DatabaseService.java | 6 +- .../session/SessionPersistentRegistry.java | 2 +- .../service/PortalInfraAuditService.java | 2 +- .../service/UnifiedAccessControlService.java | 24 +-- .../accountlink/AccountLinkController.java | 2 +- .../saas/payg/api/PaygInvoicesController.java | 2 +- .../payg/api/PaygPaymentMethodController.java | 2 +- .../saas/payg/api/PaygWalletController.java | 2 +- .../saas/payg/charge/JobChargeService.java | 2 +- .../saas/payg/lineage/JpaJobLineageStore.java | 2 +- .../SupabaseAuthenticationFilter.java | 2 +- 56 files changed, 331 insertions(+), 408 deletions(-) create mode 100644 app/core/src/test/java/stirling/software/SPDF/controller/api/UIDataControllerTest.java diff --git a/app/common/src/main/java/stirling/software/common/configuration/RuntimePathConfig.java b/app/common/src/main/java/stirling/software/common/configuration/RuntimePathConfig.java index 82921c0ba2..5be836f82a 100644 --- a/app/common/src/main/java/stirling/software/common/configuration/RuntimePathConfig.java +++ b/app/common/src/main/java/stirling/software/common/configuration/RuntimePathConfig.java @@ -73,7 +73,7 @@ public class RuntimePathConfig { defaultWatchedFolders, watchedFoldersDirs, pipeline != null ? pipeline.getWatchedFoldersDir() : null); - this.pipelineWatchedFoldersPath = this.pipelineWatchedFoldersPaths.get(0); + this.pipelineWatchedFoldersPath = this.pipelineWatchedFoldersPaths.getFirst(); this.pipelineFinishedFoldersPath = resolvePath( defaultFinishedFolders, diff --git a/app/common/src/main/java/stirling/software/common/model/oauth2/Provider.java b/app/common/src/main/java/stirling/software/common/model/oauth2/Provider.java index 185956fbb5..7a431baf26 100644 --- a/app/common/src/main/java/stirling/software/common/model/oauth2/Provider.java +++ b/app/common/src/main/java/stirling/software/common/model/oauth2/Provider.java @@ -60,54 +60,40 @@ public class Provider { } private UsernameAttribute validateUsernameAttribute(UsernameAttribute usernameAttribute) { - switch (name) { - case "google" -> { - return validateGoogleUsernameAttribute(usernameAttribute); - } - case "github" -> { - return validateGitHubUsernameAttribute(usernameAttribute); - } - case "keycloak" -> { - return validateKeycloakUsernameAttribute(usernameAttribute); - } - default -> { - return usernameAttribute; - } - } + return switch (name) { + case "google" -> validateGoogleUsernameAttribute(usernameAttribute); + case "github" -> validateGitHubUsernameAttribute(usernameAttribute); + case "keycloak" -> validateKeycloakUsernameAttribute(usernameAttribute); + default -> usernameAttribute; + }; } private UsernameAttribute validateKeycloakUsernameAttribute( UsernameAttribute usernameAttribute) { - switch (usernameAttribute) { - case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME, PREFERRED_USERNAME -> { - return usernameAttribute; - } + return switch (usernameAttribute) { + case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME, PREFERRED_USERNAME -> usernameAttribute; default -> throw new UnsupportedClaimException( String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); - } + }; } private UsernameAttribute validateGoogleUsernameAttribute(UsernameAttribute usernameAttribute) { - switch (usernameAttribute) { - case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME -> { - return usernameAttribute; - } + return switch (usernameAttribute) { + case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME -> usernameAttribute; default -> throw new UnsupportedClaimException( String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); - } + }; } private UsernameAttribute validateGitHubUsernameAttribute(UsernameAttribute usernameAttribute) { - switch (usernameAttribute) { - case LOGIN, EMAIL, NAME -> { - return usernameAttribute; - } + return switch (usernameAttribute) { + case LOGIN, EMAIL, NAME -> usernameAttribute; default -> throw new UnsupportedClaimException( String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); - } + }; } @Override diff --git a/app/common/src/main/java/stirling/software/common/pdf/PdfMarkdownConverter.java b/app/common/src/main/java/stirling/software/common/pdf/PdfMarkdownConverter.java index 73f2d7f5ad..a9932582f2 100644 --- a/app/common/src/main/java/stirling/software/common/pdf/PdfMarkdownConverter.java +++ b/app/common/src/main/java/stirling/software/common/pdf/PdfMarkdownConverter.java @@ -361,8 +361,8 @@ public class PdfMarkdownConverter { if (xs.isEmpty()) { return List.of(lines); } - float minX = xs.get(0); - float maxX = xs.get(xs.size() - 1); + float minX = xs.getFirst(); + float maxX = xs.getLast(); float splitAt = (minX + maxX) / 2f; float biggestGap = 0; for (int i = 1; i < xs.size(); i++) { @@ -492,7 +492,7 @@ public class PdfMarkdownConverter { List> anchorGroups = new ArrayList<>(); List current = new ArrayList<>(); - current.add(cands.get(0)); + current.add(cands.getFirst()); for (int i = 1; i < cands.size(); i++) { float gap = cands.get(i - 1).y - cands.get(i).y; if (gap > splitThreshold) { @@ -513,8 +513,8 @@ public class PdfMarkdownConverter { if (anchors.size() < 2) { continue; } - float top = anchors.get(0).y; - float bottom = anchors.get(anchors.size() - 1).y; + float top = anchors.getFirst().y; + float bottom = anchors.getLast().y; // Each anchor seeds a row; absorb wrapped continuation lines (non-anchors within the // run's vertical span, with a little slack below the last row) into the anchor above. @@ -674,8 +674,8 @@ public class PdfMarkdownConverter { float minGutter = Math.max(10f, charWidth * 2.5f); List merged = new ArrayList<>(); for (float[] band : columns) { - if (!merged.isEmpty() && band[0] - merged.get(merged.size() - 1)[1] < minGutter) { - merged.get(merged.size() - 1)[1] = band[1]; + if (!merged.isEmpty() && band[0] - merged.getLast()[1] < minGutter) { + merged.getLast()[1] = band[1]; } else { merged.add(new float[] {band[0], band[1]}); } @@ -734,7 +734,7 @@ public class PdfMarkdownConverter { } } StringBuilder sb = new StringBuilder(); - sb.append(buildGfmRow(rows.get(0), widths, cols)).append('\n'); + sb.append(buildGfmRow(rows.getFirst(), widths, cols)).append('\n'); sb.append('|'); for (int c = 0; c < cols; c++) { sb.append('-').append("-".repeat(widths[c])).append('-').append('|'); @@ -910,8 +910,8 @@ public class PdfMarkdownConverter { } // Only merge a sentence continuation between two text paragraphs, never into/out of a // table. - if (!(output.get(output.size() - 1) instanceof String last) - || !(pageItems.get(0) instanceof String first)) { + if (!(output.getLast() instanceof String last) + || !(pageItems.getFirst() instanceof String first)) { return; } if (!first.isEmpty() @@ -932,13 +932,13 @@ public class PdfMarkdownConverter { for (Object e : elements) { if (e instanceof TableBlock tb && !out.isEmpty() - && out.get(out.size() - 1) instanceof TableBlock prev + && out.getLast() instanceof TableBlock prev && columnsMatch(flatten(prev.rows()), flatten(tb.rows()))) { List> merged = new ArrayList<>(prev.rows()); List> tail = tb.rows(); if (!tail.isEmpty() && !prev.rows().isEmpty() - && rowText(tail.get(0)).equals(rowText(prev.rows().get(0)))) { + && rowText(tail.getFirst()).equals(rowText(prev.rows().getFirst()))) { tail = tail.subList(1, tail.size()); } merged.addAll(tail); @@ -971,7 +971,7 @@ public class PdfMarkdownConverter { continue; } if (e instanceof TableBlock tb && !tb.rows().isEmpty()) { - return rowText(tb.rows().get(0)); + return rowText(tb.rows().getFirst()); } return null; } diff --git a/app/common/src/main/java/stirling/software/common/service/InternalApiClient.java b/app/common/src/main/java/stirling/software/common/service/InternalApiClient.java index dd49ab3f51..aa9bcaa640 100644 --- a/app/common/src/main/java/stirling/software/common/service/InternalApiClient.java +++ b/app/common/src/main/java/stirling/software/common/service/InternalApiClient.java @@ -157,7 +157,7 @@ public class InternalApiClient { boolean hasFilePart = body.values().stream() .flatMap(java.util.List::stream) - .anyMatch(v -> v instanceof Resource); + .anyMatch(Resource.class::isInstance); if (isAiTool && !hasFilePart) { headers.setContentType(MediaType.MULTIPART_FORM_DATA); } diff --git a/app/common/src/main/java/stirling/software/common/util/ChecksumUtils.java b/app/common/src/main/java/stirling/software/common/util/ChecksumUtils.java index ab39ca5da8..16cd26add0 100644 --- a/app/common/src/main/java/stirling/software/common/util/ChecksumUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/ChecksumUtils.java @@ -140,20 +140,25 @@ public class ChecksumUtils { for (String algorithm : algorithms) { String key = algorithm; // keep original key for output - switch (algorithm.toUpperCase(Locale.ROOT)) { - case "CRC32": - checksums.put(key, new CRC32()); - break; - case "ADLER32": - checksums.put(key, new Adler32()); - break; - default: - try { - // For MessageDigest, pass the original name (case-insensitive per JCA) - digests.put(key, MessageDigest.getInstance(algorithm)); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Unsupported algorithm: " + algorithm, e); - } + Object digestOrChecksum = + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "CRC32" -> new CRC32(); + case "ADLER32" -> new Adler32(); + default -> { + try { + // For MessageDigest, pass the original name (case-insensitive + // per JCA) + yield MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException( + "Unsupported algorithm: " + algorithm, e); + } + } + }; + if (digestOrChecksum instanceof Checksum checksum) { + checksums.put(key, checksum); + } else { + digests.put(key, (MessageDigest) digestOrChecksum); } } diff --git a/app/common/src/main/java/stirling/software/common/util/ExceptionUtils.java b/app/common/src/main/java/stirling/software/common/util/ExceptionUtils.java index 0ec94fd51e..4aa49bd26f 100644 --- a/app/common/src/main/java/stirling/software/common/util/ExceptionUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/ExceptionUtils.java @@ -751,7 +751,7 @@ public class ExceptionUtils { String targetDescription; if (errorInfo.affectedPages() != null && !errorInfo.affectedPages().isEmpty()) { if (errorInfo.affectedPages().size() == 1) { - targetDescription = "page " + errorInfo.affectedPages().get(0); + targetDescription = "page " + errorInfo.affectedPages().getFirst(); } else { targetDescription = "pages " @@ -848,7 +848,7 @@ public class ExceptionUtils { } // Use the first page number, or null if none found - Integer pageNumber = affectedPages.isEmpty() ? null : affectedPages.get(0); + Integer pageNumber = affectedPages.isEmpty() ? null : affectedPages.getFirst(); return new GhostscriptErrorInfo( ErrorCode.GHOSTSCRIPT_PAGE_DRAWING, diff --git a/app/common/src/main/java/stirling/software/common/util/FormFieldTypeSupport.java b/app/common/src/main/java/stirling/software/common/util/FormFieldTypeSupport.java index 3018a26fd9..1f971d8d9e 100644 --- a/app/common/src/main/java/stirling/software/common/util/FormFieldTypeSupport.java +++ b/app/common/src/main/java/stirling/software/common/util/FormFieldTypeSupport.java @@ -114,7 +114,7 @@ public enum FormFieldTypeSupport { return; } - PDAnnotationWidget widget = checkBox.getWidgets().get(0); + PDAnnotationWidget widget = checkBox.getWidgets().getFirst(); PDAppearanceCharacteristicsDictionary appearanceChars = widget.getAppearanceCharacteristics(); diff --git a/app/common/src/main/java/stirling/software/common/util/FormUtils.java b/app/common/src/main/java/stirling/software/common/util/FormUtils.java index 25cb4b5825..a1862d379c 100644 --- a/app/common/src/main/java/stirling/software/common/util/FormUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/FormUtils.java @@ -88,28 +88,16 @@ public class FormUtils { * text) */ public String detectFieldType(PDField field) { - if (field instanceof PDSignatureField) { - return FIELD_TYPE_SIGNATURE; - } - if (field instanceof PDPushButton) { - return FIELD_TYPE_BUTTON; - } - if (field instanceof PDTextField) { - return FIELD_TYPE_TEXT; - } - if (field instanceof PDCheckBox) { - return FIELD_TYPE_CHECKBOX; - } - if (field instanceof PDComboBox) { - return FIELD_TYPE_COMBOBOX; - } - if (field instanceof PDListBox) { - return FIELD_TYPE_LISTBOX; - } - if (field instanceof PDRadioButton) { - return FIELD_TYPE_RADIO; - } - return FIELD_TYPE_TEXT; + return switch (field) { + case PDSignatureField ignored -> FIELD_TYPE_SIGNATURE; + case PDPushButton ignored -> FIELD_TYPE_BUTTON; + case PDTextField ignored -> FIELD_TYPE_TEXT; + case PDCheckBox ignored -> FIELD_TYPE_CHECKBOX; + case PDComboBox ignored -> FIELD_TYPE_COMBOBOX; + case PDListBox ignored -> FIELD_TYPE_LISTBOX; + case PDRadioButton ignored -> FIELD_TYPE_RADIO; + case null, default -> FIELD_TYPE_TEXT; + }; } public List extractFormFields(PDDocument document) { @@ -583,22 +571,17 @@ public class FormUtils { continue; } String type = info.type(); - Object value; - switch (type) { - case FIELD_TYPE_CHECKBOX: - value = isChecked(info.value()) ? Boolean.TRUE : Boolean.FALSE; - break; - case FIELD_TYPE_LISTBOX: - if (info.multiSelect()) { - value = new ArrayList<>(); - } else { - value = safeDefault(info.value()); - } - break; - case FIELD_TYPE_BUTTON, FIELD_TYPE_SIGNATURE: - continue; // skip non-fillable - default: - value = safeDefault(info.value()); + Object value = + switch (type) { + case FIELD_TYPE_CHECKBOX -> + isChecked(info.value()) ? Boolean.TRUE : Boolean.FALSE; + case FIELD_TYPE_LISTBOX -> + info.multiSelect() ? new ArrayList<>() : safeDefault(info.value()); + case FIELD_TYPE_BUTTON, FIELD_TYPE_SIGNATURE -> null; + default -> safeDefault(info.value()); + }; + if (value == null) { + continue; // skip non-fillable } record.put(info.name(), value); } @@ -949,44 +932,44 @@ public class FormUtils { if (selection == null || selection.trim().isEmpty()) return null; List filtered = filterChoiceSelections(List.of(selection), allowedOptions, fieldName); - return filtered.isEmpty() ? null : filtered.get(0); + return filtered.isEmpty() ? null : filtered.getFirst(); } private void applyValueToField(PDField field, String value, boolean strict) throws IOException { try { - if (field instanceof PDTextField textField) { - setTextValue(textField, value); - } else if (field instanceof PDCheckBox checkBox) { - LinkedHashSet candidateStates = collectCheckBoxStates(checkBox); - boolean shouldCheck = shouldCheckBoxBeChecked(value, candidateStates); - try { - if (shouldCheck) { - checkBox.check(); - } else { - checkBox.unCheck(); - } - } catch (IOException checkProblem) { - log.warn( - "Failed to set checkbox state for '{}': {}", - field.getFullyQualifiedName(), - checkProblem.getMessage(), - checkProblem); - if (strict) { - throw checkProblem; + switch (field) { + case PDTextField textField -> setTextValue(textField, value); + case PDCheckBox checkBox -> { + LinkedHashSet candidateStates = collectCheckBoxStates(checkBox); + boolean shouldCheck = shouldCheckBoxBeChecked(value, candidateStates); + try { + if (shouldCheck) { + checkBox.check(); + } else { + checkBox.unCheck(); + } + } catch (IOException checkProblem) { + log.warn( + "Failed to set checkbox state for '{}': {}", + field.getFullyQualifiedName(), + checkProblem.getMessage(), + checkProblem); + if (strict) { + throw checkProblem; + } } } - } else if (field instanceof PDRadioButton radioButton) { - if (value != null && !value.isBlank()) { - radioButton.setValue(value); + case PDRadioButton radioButton -> { + if (value != null && !value.isBlank()) { + radioButton.setValue(value); + } } - } else if (field instanceof PDChoice choiceField) { - applyChoiceValue(choiceField, value); - } else if (field instanceof PDPushButton) { - log.debug("Ignore Push button"); - } else if (field instanceof PDSignatureField) { - log.debug("Skipping signature field '{}'", field.getFullyQualifiedName()); - } else { - field.setValue(value != null ? value : ""); + case PDChoice choiceField -> applyChoiceValue(choiceField, value); + case PDPushButton ignored -> log.debug("Ignore Push button"); + case PDSignatureField ignored -> + log.debug("Skipping signature field '{}'", field.getFullyQualifiedName()); + case null -> log.warn("Attempted to set value on null field"); + default -> field.setValue(value != null ? value : ""); } } catch (Exception e) { log.warn( @@ -1306,37 +1289,42 @@ public class FormUtils { List resolveOptions(PDTerminalField field) { try { - if (field instanceof PDChoice choice) { - LinkedHashSet allowed = new LinkedHashSet<>(); - List exportValues = choice.getOptionsExportValues(); - List displayValues = choice.getOptionsDisplayValues(); + return switch (field) { + case PDChoice choice -> { + LinkedHashSet allowed = new LinkedHashSet<>(); + List exportValues = choice.getOptionsExportValues(); + List displayValues = choice.getOptionsDisplayValues(); - if (exportValues != null) { - exportValues.stream() - .filter(Objects::nonNull) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .forEach(allowed::add); + if (exportValues != null) { + exportValues.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .forEach(allowed::add); + } + if (displayValues != null) { + displayValues.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .forEach(allowed::add); + } + yield new ArrayList<>(allowed); } - if (displayValues != null) { - displayValues.stream() - .filter(Objects::nonNull) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .forEach(allowed::add); + case PDRadioButton radio -> { + List exports = radio.getExportValues(); + yield exports != null && !exports.isEmpty() + ? new ArrayList<>(exports) + : Collections.emptyList(); } - return new ArrayList<>(allowed); - } else if (field instanceof PDRadioButton radio) { - List exports = radio.getExportValues(); - if (exports != null && !exports.isEmpty()) { - return new ArrayList<>(exports); + case PDCheckBox checkBox -> { + List exports = checkBox.getExportValues(); + yield exports != null && !exports.isEmpty() + ? new ArrayList<>(exports) + : Collections.emptyList(); } - } else if (field instanceof PDCheckBox checkBox) { - List exports = checkBox.getExportValues(); - if (exports != null && !exports.isEmpty()) { - return new ArrayList<>(exports); - } - } + case null, default -> Collections.emptyList(); + }; } catch (Exception e) { log.debug( "Failed to resolve options for field '{}': {}", @@ -1465,7 +1453,7 @@ public class FormUtils { // Only check options for choice-type fields (combobox, listbox, radio) if (CHOICE_FIELD_TYPES.contains(type) && options != null && !options.isEmpty()) { - String optionCandidate = cleanLabel(options.get(0)); + String optionCandidate = cleanLabel(options.getFirst()); if (optionCandidate != null && !looksGeneric(optionCandidate)) { return optionCandidate; } @@ -1557,7 +1545,7 @@ public class FormUtils { continue; } - PDAnnotationWidget widget = widgets.get(0); + PDAnnotationWidget widget = widgets.getFirst(); PDRectangle originalRectangle = cloneRectangle(widget.getRectangle()); PDPage page = resolveWidgetPage(document, widget, null); if (page == null || originalRectangle == null) { @@ -2446,19 +2434,19 @@ public class FormUtils { private static int firstWidgetPageIndex(FormFieldWithCoordinates f) { return (f.getWidgets() != null && !f.getWidgets().isEmpty()) - ? f.getWidgets().get(0).getPageIndex() + ? f.getWidgets().getFirst().getPageIndex() : -1; } private static float firstWidgetY(FormFieldWithCoordinates f) { return (f.getWidgets() != null && !f.getWidgets().isEmpty()) - ? f.getWidgets().get(0).getY() + ? f.getWidgets().getFirst().getY() : 0; } private static float firstWidgetX(FormFieldWithCoordinates f) { return (f.getWidgets() != null && !f.getWidgets().isEmpty()) - ? f.getWidgets().get(0).getX() + ? f.getWidgets().getFirst().getX() : 0; } diff --git a/app/common/src/main/java/stirling/software/common/util/ImageProcessingUtils.java b/app/common/src/main/java/stirling/software/common/util/ImageProcessingUtils.java index 979ad25e99..030751f3e3 100644 --- a/app/common/src/main/java/stirling/software/common/util/ImageProcessingUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/ImageProcessingUtils.java @@ -26,29 +26,27 @@ import lombok.extern.slf4j.Slf4j; public class ImageProcessingUtils { static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) { - BufferedImage convertedImage; - switch (colorType) { - case "greyscale": - convertedImage = + return switch (colorType) { + case "greyscale" -> { + BufferedImage convertedImage = new BufferedImage( sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY); convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null); - break; - case "blackwhite": - convertedImage = + yield convertedImage; + } + case "blackwhite" -> { + BufferedImage convertedImage = new BufferedImage( sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY); convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null); - break; - default: // full color - convertedImage = sourceImage; - break; - } - return convertedImage; + yield convertedImage; + } + default -> sourceImage; + }; } public static byte[] getImageData(BufferedImage image) { diff --git a/app/common/src/main/java/stirling/software/common/util/PDFToFile.java b/app/common/src/main/java/stirling/software/common/util/PDFToFile.java index feac968c55..ce164e9db2 100644 --- a/app/common/src/main/java/stirling/software/common/util/PDFToFile.java +++ b/app/common/src/main/java/stirling/software/common/util/PDFToFile.java @@ -330,7 +330,7 @@ public class PDFToFile { if (outputFiles.size() == 1) { // Return single output file - File outputFile = outputFiles.get(0); + File outputFile = outputFiles.getFirst(); if ("txt:Text".equals(outputFormat)) { outputFormat = "txt"; } diff --git a/app/common/src/main/java/stirling/software/common/util/ProcessExecutor.java b/app/common/src/main/java/stirling/software/common/util/ProcessExecutor.java index eb36711c0f..1a8786bf1d 100644 --- a/app/common/src/main/java/stirling/software/common/util/ProcessExecutor.java +++ b/app/common/src/main/java/stirling/software/common/util/ProcessExecutor.java @@ -307,7 +307,7 @@ public class ProcessExecutor { boolean isQpdf = commandToRun != null && !commandToRun.isEmpty() - && commandToRun.get(0).contains("qpdf"); + && commandToRun.getFirst().contains("qpdf"); if (!outputLines.isEmpty()) { String outputMessage = String.join("\n", outputLines); @@ -370,7 +370,7 @@ public class ProcessExecutor { } // Check if this is a UNO conversion by looking for unoconvert executable - String executable = command.get(0); + String executable = command.getFirst(); if (executable != null) { // Extract basename from path for matching String basename = executable; @@ -504,7 +504,7 @@ public class ProcessExecutor { } // Validate executable (first argument) - String executable = command.get(0); + String executable = command.getFirst(); if (executable == null || executable.isBlank()) { throw new IllegalArgumentException("Command executable must not be empty"); } diff --git a/app/common/src/main/java/stirling/software/common/util/YamlHelper.java b/app/common/src/main/java/stirling/software/common/util/YamlHelper.java index 8fb6896f67..2715f50dfb 100644 --- a/app/common/src/main/java/stirling/software/common/util/YamlHelper.java +++ b/app/common/src/main/java/stirling/software/common/util/YamlHelper.java @@ -114,7 +114,7 @@ public class YamlHelper { for (NodeTuple tuple : mappingNode.getValue()) { ScalarNode keyNode = (tuple.getKeyNode() instanceof ScalarNode sk) ? sk : null; - if (keyNode == null || !keyNode.getValue().equals(keys.get(0))) { + if (keyNode == null || !keyNode.getValue().equals(keys.getFirst())) { updatedTuples.add(tuple); continue; } diff --git a/app/common/src/test/java/stirling/software/common/util/PDFToFileTest.java b/app/common/src/test/java/stirling/software/common/util/PDFToFileTest.java index 4ec7164514..b3491f7183 100644 --- a/app/common/src/test/java/stirling/software/common/util/PDFToFileTest.java +++ b/app/common/src/test/java/stirling/software/common/util/PDFToFileTest.java @@ -721,7 +721,7 @@ class PDFToFileTest { .thenAnswer( invocation -> { List args = invocation.getArgument(0); - String outputPath = args.get(args.size() - 1); + String outputPath = args.getLast(); Files.write(Path.of(outputPath), "Fake DOCX content".getBytes()); return mockExecutorResult; }); diff --git a/app/core/src/main/java/stirling/software/SPDF/config/MultipartConfiguration.java b/app/core/src/main/java/stirling/software/SPDF/config/MultipartConfiguration.java index 9625f9e671..ff6d1b87bd 100644 --- a/app/core/src/main/java/stirling/software/SPDF/config/MultipartConfiguration.java +++ b/app/core/src/main/java/stirling/software/SPDF/config/MultipartConfiguration.java @@ -1,6 +1,5 @@ package stirling.software.SPDF.config; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.servlet.MultipartConfigFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,7 +21,11 @@ import stirling.software.SPDF.controller.web.UploadLimitService; @Slf4j public class MultipartConfiguration { - @Autowired private UploadLimitService uploadLimitService; + private final UploadLimitService uploadLimitService; + + public MultipartConfiguration(UploadLimitService uploadLimitService) { + this.uploadLimitService = uploadLimitService; + } /** * Creates MultipartConfigElement that respects fileUploadLimit from settings.yml or environment diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/BookletImpositionController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/BookletImpositionController.java index f777680223..5dbccb649d 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/BookletImpositionController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/BookletImpositionController.java @@ -297,24 +297,25 @@ public class BookletImpositionController { // Apply rotation if needed (rotate about origin), then translate to keep in cell switch (rot) { - case 90: + case 90 -> { cs.transform(Matrix.getRotateInstance(Math.PI / 2, 0, 0)); // After 90° CCW, the content spans x in [-r.getHeight(), 0] and y in [0, // r.getWidth()] cs.transform(Matrix.getTranslateInstance(0, -r.getWidth())); - break; - case 180: + } + case 180 -> { cs.transform(Matrix.getRotateInstance(Math.PI, 0, 0)); cs.transform(Matrix.getTranslateInstance(-r.getWidth(), -r.getHeight())); - break; - case 270: + } + case 270 -> { cs.transform(Matrix.getRotateInstance(3 * Math.PI / 2, 0, 0)); // After 270° CCW, the content spans x in [0, r.getHeight()] and y in // [-r.getWidth(), 0] cs.transform(Matrix.getTranslateInstance(-r.getHeight(), 0)); - break; - default: + } + default -> { // 0°: no-op + } } // Reuse LayerUtility passed from caller diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index db26ca58d1..77a97b9b05 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; -import java.util.regex.Pattern; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; @@ -61,8 +60,6 @@ import stirling.software.jpdfium.doc.PdfBookmarkEditor.BookmarkTree; @Slf4j @RequiredArgsConstructor public class MergeController { - - private static final Pattern QUOTE_WRAP_PATTERN = Pattern.compile("^\"|\"$"); private final CustomPDFDocumentFactory pdfDocumentFactory; private final TempFileManager tempFileManager; @@ -164,30 +161,6 @@ public class MergeController { }; } - private String[] parseClientFileIds(String clientFileIds) { - if (clientFileIds == null || clientFileIds.trim().isEmpty()) { - return new String[0]; - } - try { - String trimmed = clientFileIds.trim(); - if (trimmed.startsWith("[") && trimmed.endsWith("]")) { - String inside = trimmed.substring(1, trimmed.length() - 1).trim(); - if (inside.isEmpty()) { - return new String[0]; - } - String[] parts = inside.split(","); - String[] result = new String[parts.length]; - for (int i = 0; i < parts.length; i++) { - result[i] = QUOTE_WRAP_PATTERN.matcher(parts[i].trim()).replaceAll(""); - } - return result; - } - } catch (Exception e) { - log.warn("Failed to parse client file IDs: {}", clientFileIds, e); - } - return new String[0]; - } - private void addTableOfContents(PDDocument mergedDocument, MultipartFile[] files) { PDDocumentOutline outline = new PDDocumentOutline(); mergedDocument.getDocumentCatalog().setDocumentOutline(outline); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java index a3ed09fe5d..7f93fb3d64 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java @@ -125,17 +125,14 @@ public class UIDataController { pipelineConfigs.add(content); } - for (String config : pipelineConfigs) { + for (int i = 0; i < jsonFiles.size(); i++) { + String config = pipelineConfigs.get(i); Map jsonContent = objectMapper.readValue( config, new TypeReference>() {}); String name = (String) jsonContent.get("name"); if (name == null || name.isEmpty()) { - String filename = - jsonFiles - .get(pipelineConfigs.indexOf(config)) - .getFileName() - .toString(); + String filename = jsonFiles.get(i).getFileName().toString(); name = filename.substring(0, filename.lastIndexOf('.')); } Map configWithName = new HashMap<>(); @@ -301,20 +298,14 @@ public class UIDataController { } private static String getFormatFromExtension(String extension) { - switch (extension) { - case "ttf": - return "truetype"; - case "woff": - return "woff"; - case "woff2": - return "woff2"; - case "eot": - return "embedded-opentype"; - case "svg": - return "svg"; - default: - return ""; - } + return switch (extension) { + case "ttf" -> "truetype"; + case "woff" -> "woff"; + case "woff2" -> "woff2"; + case "eot" -> "embedded-opentype"; + case "svg" -> "svg"; + default -> ""; + }; } } } diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index 40f1c8be45..b30e8d8c4d 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -200,7 +200,7 @@ public class ConvertImgPDFController { } if (webpFiles.size() == 1) { - Path webpFilePath = webpFiles.get(0); + Path webpFilePath = webpFiles.getFirst(); byte[] webpBytes = Files.readAllBytes(webpFilePath); Files.deleteIfExists(tempFile); tempFile = null; diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertSvgToPDF.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertSvgToPDF.java index 94a9ed44f2..0b1bfc8e44 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertSvgToPDF.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertSvgToPDF.java @@ -160,7 +160,7 @@ public class ConvertSvgToPDF { String outputFilename = filenames.isEmpty() ? "combined_svgs.pdf" - : GeneralUtils.generateFilename(filenames.get(0), "_combined.pdf"); + : GeneralUtils.generateFilename(filenames.getFirst(), "_combined.pdf"); log.info("Successfully combined {} SVGs into single PDF", sanitizedSvgs.size()); @@ -216,7 +216,7 @@ public class ConvertSvgToPDF { try { if (convertedPdfs.size() == 1) { - ConvertedPdf pdf = convertedPdfs.get(0); + ConvertedPdf pdf = convertedPdfs.getFirst(); TempFile tempOut = tempFileManager.createManagedTempFile(".pdf"); try { Files.write(tempOut.getPath(), pdf.content); @@ -231,7 +231,7 @@ public class ConvertSvgToPDF { filenames.isEmpty() ? "converted_svgs.zip" : GeneralUtils.generateFilename( - filenames.get(0), "_converted_svgs.zip"); + filenames.getFirst(), "_converted_svgs.zip"); TempFile zipFile = createZipFromPdfs(convertedPdfs); return WebResponseUtils.zipFileToWebResponse(zipFile, zipFilename); } catch (IOException e) { diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java index 1141d503aa..22dff8f996 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java @@ -85,7 +85,7 @@ public class ExtractCSVController { if (csvEntries.isEmpty()) { return ResponseEntity.noContent().build(); } else if (csvEntries.size() == 1) { - return createCsvResponse(csvEntries.get(0), baseName); + return createCsvResponse(csvEntries.getFirst(), baseName); } else { return createZipResponse(csvEntries, baseName); } diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java index 60333191d3..34c3da0739 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java @@ -116,7 +116,9 @@ public class AutoRenameController { mergedLineInfos.sort( Comparator.comparing((LineInfo li) -> li.fontSize).reversed()); String title = - mergedLineInfos.isEmpty() ? null : mergedLineInfos.get(0).text; + mergedLineInfos.isEmpty() + ? null + : mergedLineInfos.getFirst().text; return title != null ? title diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java index 39ba6e54f5..f513391d7a 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java @@ -336,7 +336,7 @@ public class AutoSplitPdfController { } if (!splitDocuments.isEmpty() && !isValidQrCode) { - splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page)); + splitDocuments.getLast().addPage(document.getPage(page)); } else if (page == 0) { PDDocument firstDocument = new PDDocument(); firstDocument.addPage(document.getPage(page)); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index fcbfcea62e..cd2b809760 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -269,7 +269,7 @@ public class CompressController { if (references.isEmpty()) continue; // Get the first instance of this image - PDImageXObject originalImage = getOriginalImage(doc, references.get(0)); + PDImageXObject originalImage = getOriginalImage(doc, references.getFirst()); // Track original size int originalSize = (int) originalImage.getCOSObject().getLength(); @@ -1170,7 +1170,7 @@ public class CompressController { List references = entry.getValue(); if (references.isEmpty()) continue; - PDImageXObject originalImage = getOriginalImage(doc, references.get(0)); + PDImageXObject originalImage = getOriginalImage(doc, references.getFirst()); int originalSize = (int) originalImage.getCOSObject().getLength(); stats.totalOriginalBytes += originalSize; diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java index daa8f35121..816b95d604 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java @@ -214,7 +214,7 @@ public class ExtractImageScansController { } else { // Return the processed image as a response - byte[] imageBytes = processedImageBytes.get(0); + byte[] imageBytes = processedImageBytes.getFirst(); finalOutput = tempFileManager.createManagedTempFile(".png"); try (OutputStream out = Files.newOutputStream(finalOutput.getPath())) { out.write(imageBytes); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index c268f48d8c..58acfda82d 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -407,7 +407,7 @@ public class CertSignController { PDAcroForm acroForm = new PDAcroForm(doc); doc.getDocumentCatalog().setAcroForm(acroForm); PDSignatureField signatureField = new PDSignatureField(acroForm); - PDAnnotationWidget widget = signatureField.getWidgets().get(0); + PDAnnotationWidget widget = signatureField.getWidgets().getFirst(); List acroFormFields = acroForm.getFields(); acroForm.setSignaturesExist(true); acroForm.setAppendOnly(true); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactExecuteService.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactExecuteService.java index c43abcf666..10fceee45f 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactExecuteService.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactExecuteService.java @@ -634,7 +634,7 @@ class RedactExecuteService { PageColumnLayout layout = PageColumnLayout.fromLineBoxes(extractor.getLineBoxes(), pageWidth); if (layout.columnCount() > 1) { - float[] g = layout.gutters().get(0); + float[] g = layout.gutters().getFirst(); log.info( "[redact/execute] page {} layout: 2 cols, gutter x=[{}, {}]", pageIdx + 1, diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java index bb1e0e48d5..4b0cc6e6b0 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java @@ -63,7 +63,7 @@ public class RemoveCertSignController { // Remove signature fields safely List fieldsToRemove = acroForm.getFields().stream() - .filter(field -> field instanceof PDSignatureField) + .filter(PDSignatureField.class::isInstance) .toList(); if (!fieldsToRemove.isEmpty()) { diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java b/app/core/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java index a15f14af98..7065ac4d79 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java @@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.web; import java.util.Locale; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; @@ -13,7 +12,11 @@ import stirling.software.common.model.ApplicationProperties; @Slf4j public class UploadLimitService { - @Autowired private ApplicationProperties applicationProperties; + private final ApplicationProperties applicationProperties; + + public UploadLimitService(ApplicationProperties applicationProperties) { + this.applicationProperties = applicationProperties; + } public long getUploadLimit() { String raw = diff --git a/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java index fea3b52fc5..bc2e49853e 100644 --- a/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java +++ b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java @@ -2688,7 +2688,7 @@ public class PdfJsonConversionService { // Find which page the field is on PDAnnotationWidget widget = - field.getWidgets().isEmpty() ? null : field.getWidgets().get(0); + field.getWidgets().isEmpty() ? null : field.getWidgets().getFirst(); if (widget != null) { PDPage fieldPage = widget.getPage(); if (fieldPage != null) { @@ -3164,7 +3164,7 @@ public class PdfJsonConversionService { && imageObjectNames != null && !imageObjectNames.isEmpty() && !targetTokens.isEmpty()) { - Object previous = targetTokens.get(targetTokens.size() - 1); + Object previous = targetTokens.getLast(); if (previous instanceof COSName cosName && imageObjectNames.contains(cosName.getName())) { targetTokens.remove(targetTokens.size() - 1); @@ -5246,7 +5246,7 @@ public class PdfJsonConversionService { throws IOException { if (OperatorName.DRAW_OBJECT.equals(operator.getName()) && !operands.isEmpty() - && operands.get(0) instanceof COSName name) { + && operands.getFirst() instanceof COSName name) { currentXObjectName = name; } super.processOperator(operator, operands); diff --git a/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java index d0a0ad2dae..4ac18596f2 100644 --- a/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java +++ b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java @@ -420,7 +420,7 @@ public class PdfJsonImageService { throws IOException { if (OperatorName.DRAW_OBJECT.equals(operator.getName()) && !operands.isEmpty() - && operands.get(0) instanceof COSName name) { + && operands.getFirst() instanceof COSName name) { currentXObjectName = name; } super.processOperator(operator, operands); diff --git a/app/core/src/main/java/stirling/software/common/controller/JobController.java b/app/core/src/main/java/stirling/software/common/controller/JobController.java index bab5dce539..ef9e81873e 100644 --- a/app/core/src/main/java/stirling/software/common/controller/JobController.java +++ b/app/core/src/main/java/stirling/software/common/controller/JobController.java @@ -137,7 +137,7 @@ public class JobController { if (result.hasFiles() && !result.hasMultipleFiles()) { try { List files = result.getAllResultFiles(); - ResultFile singleFile = files.get(0); + ResultFile singleFile = files.getFirst(); byte[] fileContent = fileStorage.retrieveBytes(singleFile.getFileId()); return ResponseEntity.ok() diff --git a/app/core/src/test/java/stirling/software/SPDF/config/MultipartConfigurationTest.java b/app/core/src/test/java/stirling/software/SPDF/config/MultipartConfigurationTest.java index 60b17a3534..276afbbe99 100644 --- a/app/core/src/test/java/stirling/software/SPDF/config/MultipartConfigurationTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/config/MultipartConfigurationTest.java @@ -4,8 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.lang.reflect.Field; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -24,10 +22,7 @@ class MultipartConfigurationTest { void setUp() throws Exception { // Manually constructed config with a mocked service, so Spring env overrides do not apply. uploadLimitService = mock(UploadLimitService.class); - configuration = new MultipartConfiguration(); - Field field = MultipartConfiguration.class.getDeclaredField("uploadLimitService"); - field.setAccessible(true); - field.set(configuration, uploadLimitService); + configuration = new MultipartConfiguration(uploadLimitService); } @Nested diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/MergeControllerGapTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/MergeControllerGapTest.java index f5543bcdc7..0a6aa0efca 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/MergeControllerGapTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/MergeControllerGapTest.java @@ -84,12 +84,6 @@ class MergeControllerGapTest { return (MultipartFile[]) m.invoke(null, files, fileOrder); } - private String[] parseClientFileIds(String value) throws Exception { - Method m = MergeController.class.getDeclaredMethod("parseClientFileIds", String.class); - m.setAccessible(true); - return (String[]) m.invoke(mergeController, value); - } - private long getPdfDateTimeSafe(MultipartFile file) throws Exception { Method m = MergeController.class.getDeclaredMethod("getPdfDateTimeSafe", MultipartFile.class); @@ -331,58 +325,6 @@ class MergeControllerGapTest { } } - // ---- parseClientFileIds ------------------------------------------------- - - @Nested - @DisplayName("parseClientFileIds") - class ParseClientFileIds { - - @Test - @DisplayName("null input returns empty array") - void nullReturnsEmpty() throws Exception { - assertEquals(0, parseClientFileIds(null).length); - } - - @Test - @DisplayName("blank input returns empty array") - void blankReturnsEmpty() throws Exception { - assertEquals(0, parseClientFileIds(" ").length); - } - - @Test - @DisplayName("empty JSON array returns empty array") - void emptyArrayReturnsEmpty() throws Exception { - assertEquals(0, parseClientFileIds("[]").length); - assertEquals(0, parseClientFileIds("[ ]").length); - } - - @Test - @DisplayName("non-array text returns empty array") - void nonArrayReturnsEmpty() throws Exception { - assertEquals(0, parseClientFileIds("not-an-array").length); - } - - @Test - @DisplayName("parses quoted, comma-separated ids and strips surrounding quotes") - void parsesQuotedIds() throws Exception { - String[] result = parseClientFileIds("[\"id1\", \"id2\",\"id3\"]"); - assertArrayEquals(new String[] {"id1", "id2", "id3"}, result); - } - - @Test - @DisplayName("parses unquoted ids as-is after trimming") - void parsesUnquotedIds() throws Exception { - String[] result = parseClientFileIds("[a, b , c]"); - assertArrayEquals(new String[] {"a", "b", "c"}, result); - } - - @Test - @DisplayName("single element array yields a one-element result") - void singleElement() throws Exception { - assertArrayEquals(new String[] {"only"}, parseClientFileIds("[\"only\"]")); - } - } - // ---- reorderFilesByProvidedOrder ---------------------------------------- @Nested diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/UIDataControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/UIDataControllerTest.java new file mode 100644 index 0000000000..33e2fbf0df --- /dev/null +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/UIDataControllerTest.java @@ -0,0 +1,66 @@ +package stirling.software.SPDF.controller.api; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.http.ResponseEntity; + +import stirling.software.SPDF.service.SharedSignatureService; +import stirling.software.common.configuration.RuntimePathConfig; +import stirling.software.common.model.ApplicationProperties; + +import tools.jackson.databind.ObjectMapper; + +class UIDataControllerTest { + + @TempDir Path tempDir; + + @Test + void getPipelineData_usesEachSourceFilenameWhenJsonContentIsIdentical() throws Exception { + Path configDir = tempDir.resolve("defaultWebUIConfigs"); + Files.createDirectories(configDir); + + String configJson = "{\"steps\":[]}"; + Files.writeString(configDir.resolve("first-config.json"), configJson); + Files.writeString(configDir.resolve("second-config.json"), configJson); + + ApplicationProperties applicationProperties = mock(ApplicationProperties.class); + SharedSignatureService signatureService = mock(SharedSignatureService.class); + RuntimePathConfig runtimePathConfig = mock(RuntimePathConfig.class); + + when(runtimePathConfig.getPipelineDefaultWebUiConfigs()).thenReturn(configDir.toString()); + + UIDataController controller = + new UIDataController( + applicationProperties, + signatureService, + null, + new DefaultResourceLoader(), + runtimePathConfig, + new ObjectMapper()); + + ResponseEntity response = controller.getPipelineData(); + + assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); + UIDataController.PipelineData body = response.getBody(); + assertThat(body).isNotNull(); + + List> configsWithNames = body.getPipelineConfigsWithNames(); + assertThat(configsWithNames).hasSize(2); + assertThat(configsWithNames) + .extracting(entry -> entry.get("name")) + .containsExactlyInAnyOrder("first-config", "second-config"); + assertThat(configsWithNames) + .extracting(entry -> entry.get("json")) + .containsOnly(configJson); + } +} diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeControllerTest.java index 90b6a370de..14c49b351a 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeControllerTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeControllerTest.java @@ -204,7 +204,7 @@ class ConvertOfficeControllerTest { inv -> { // unoconvert writes directly to the output path (last arg) List command = inv.getArgument(0); - Path out = Path.of(command.get(command.size() - 1)); + Path out = Path.of(command.getLast()); Files.writeString(out, "%PDF-1.4 produced"); return result; }); @@ -239,7 +239,7 @@ class ConvertOfficeControllerTest { inv -> { // soffice writes .pdf into the --outdir (workDir) List command = inv.getArgument(0); - Path inputPath = Path.of(command.get(command.size() - 1)); + Path inputPath = Path.of(command.getLast()); Path out = inputPath.getParent().resolve("report.pdf"); Files.writeString(out, "%PDF soffice"); return result; @@ -311,7 +311,7 @@ class ConvertOfficeControllerTest { .thenAnswer( inv -> { List command = inv.getArgument(0); - Path inputPath = Path.of(command.get(command.size() - 1)); + Path inputPath = Path.of(command.getLast()); Path out = inputPath.getParent().resolve("report.pdf"); Files.write(out, new byte[0]); return result; @@ -344,7 +344,7 @@ class ConvertOfficeControllerTest { .thenAnswer( inv -> { List command = inv.getArgument(0); - Path inputPath = Path.of(command.get(command.size() - 1)); + Path inputPath = Path.of(command.getLast()); Path out = inputPath.getParent().resolve("page.pdf"); Files.writeString(out, "%PDF html"); return result; @@ -398,7 +398,7 @@ class ConvertOfficeControllerTest { .thenAnswer( inv -> { List command = inv.getArgument(0); - Path inputPath = Path.of(command.get(command.size() - 1)); + Path inputPath = Path.of(command.getLast()); Path out = inputPath.getParent().resolve("report.pdf"); Files.writeString(out, "%PDF produced"); return result; diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFAMoreTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFAMoreTest.java index 31bbc9e1f8..e9d9b9ce1d 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFAMoreTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFAMoreTest.java @@ -197,7 +197,7 @@ class ConvertPDFToPDFAMoreTest { // qpdf normalize/clean writes its (last-arg) output file if (command.contains("--normalize-content=y")) { // qpdf produced file is the last argument - Path out = Path.of(command.get(command.size() - 1)); + Path out = Path.of(command.getLast()); Files.write(out, simplePdfBytes()); } return okResult; diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPdfToVideoControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPdfToVideoControllerTest.java index 7a46611443..52104df48e 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPdfToVideoControllerTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertPdfToVideoControllerTest.java @@ -262,7 +262,7 @@ class ConvertPdfToVideoControllerTest { assertTrue(command.contains("+faststart")); assertFalse(command.contains("libvpx-vp9")); // Output path is always the last argument. - assertEquals(backing.getAbsolutePath(), command.get(command.size() - 1)); + assertEquals(backing.getAbsolutePath(), command.getLast()); } @Test @@ -278,7 +278,7 @@ class ConvertPdfToVideoControllerTest { assertTrue(command.contains("30")); assertFalse(command.contains("libx264")); assertFalse(command.contains("+faststart")); - assertEquals(backing.getAbsolutePath(), command.get(command.size() - 1)); + assertEquals(backing.getAbsolutePath(), command.getLast()); } @Test diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/CompressControllerMoreTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/CompressControllerMoreTest.java index b8f4e51b3f..4393c19e3f 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/CompressControllerMoreTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/CompressControllerMoreTest.java @@ -195,7 +195,7 @@ class CompressControllerMoreTest { // The qpdf output path is the last argument of the command. private static Path qpdfOutputPath(List command) { - return Path.of(command.get(command.size() - 1)); + return Path.of(command.getLast()); } /** Stub gs to write a valid PDF to its output file and report success. */ diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RemoveImagesControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RemoveImagesControllerTest.java index 1a97cc1a55..db361b4302 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RemoveImagesControllerTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RemoveImagesControllerTest.java @@ -162,7 +162,7 @@ class RemoveImagesControllerTest { /** Counts every PDImageXObject reachable through page + nested form resources. */ private int countImagesInSavedOutput() throws IOException { assertFalse(savedTempFiles.isEmpty(), "expected the controller to create a temp file"); - File out = savedTempFiles.get(savedTempFiles.size() - 1); + File out = savedTempFiles.getLast(); try (PDDocument doc = Loader.loadPDF(out)) { int count = 0; for (PDPage page : doc.getPages()) { @@ -245,7 +245,7 @@ class RemoveImagesControllerTest { assertEquals(0, countImagesInSavedOutput()); // page count must be preserved - File out = savedTempFiles.get(savedTempFiles.size() - 1); + File out = savedTempFiles.getLast(); try (PDDocument result = Loader.loadPDF(out)) { assertEquals(3, result.getNumberOfPages()); } diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RepairControllerMoreTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RepairControllerMoreTest.java index 6565a84875..f564dafb11 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RepairControllerMoreTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/RepairControllerMoreTest.java @@ -98,12 +98,8 @@ class RepairControllerMoreTest { } } - /** - * Writes a valid PDF to the path at the given command index, mimicking a successful tool run. - */ - private static void writeValidPdfTo(List command, int outputPathIndex) - throws Exception { - Path out = Path.of(command.get(outputPathIndex)); + /** Writes a valid PDF to the given output path, mimicking a successful tool run. */ + private static void writeValidPdfTo(Path out) throws Exception { byte[] pdf = buildPdfBytes(1); Files.write(out, pdf); } @@ -133,7 +129,7 @@ class RepairControllerMoreTest { .thenAnswer( inv -> { List cmd = inv.getArgument(0); - writeValidPdfTo(cmd, 2); + writeValidPdfTo(Path.of(cmd.get(2))); return okResult; }); @@ -176,7 +172,7 @@ class RepairControllerMoreTest { .thenAnswer( inv -> { List cmd = inv.getArgument(0); - writeValidPdfTo(cmd, cmd.size() - 1); + writeValidPdfTo(Path.of(cmd.getLast())); return okResult; }); @@ -216,7 +212,7 @@ class RepairControllerMoreTest { .thenAnswer( inv -> { List cmd = inv.getArgument(0); - writeValidPdfTo(cmd, cmd.size() - 1); + writeValidPdfTo(Path.of(cmd.getLast())); return okResult; }); @@ -256,7 +252,7 @@ class RepairControllerMoreTest { .thenAnswer( inv -> { List cmd = inv.getArgument(0); - writeValidPdfTo(cmd, cmd.size() - 1); + writeValidPdfTo(Path.of(cmd.getLast())); return okResult; }); diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java index 219affcd30..173741df87 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java @@ -49,15 +49,7 @@ class UploadLimitServiceTest { systemProps = mock(ApplicationProperties.System.class); when(applicationProperties.getSystem()).thenReturn(systemProps); - uploadLimitService = new UploadLimitService(); - // inject mock - try { - var field = UploadLimitService.class.getDeclaredField("applicationProperties"); - field.setAccessible(true); - field.set(uploadLimitService, applicationProperties); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + uploadLimitService = new UploadLimitService(applicationProperties); } @ParameterizedTest(name = "getReadableUploadLimit case #{index}: rawValue={0}, expected={1}") diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/AuditRestController.java b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/AuditRestController.java index 69c9402926..7376795c66 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/AuditRestController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/AuditRestController.java @@ -143,19 +143,12 @@ public class AuditRestController { @RequestParam(value = "period", defaultValue = "week") String period) { // Calculate days based on period - int days; - switch (period.toLowerCase()) { - case "day": - days = 1; - break; - case "month": - days = 30; - break; - case "week": - default: - days = 7; - break; - } + int days = + switch (period.toLowerCase()) { + case "day" -> 1; + case "month" -> 30; + default -> 7; + }; // Get events from the specified period Instant startDate = Instant.now().minus(java.time.Duration.ofDays(days)); @@ -269,19 +262,12 @@ public class AuditRestController { @RequestParam(value = "period", defaultValue = "week") String period) { // Calculate days based on period - int days; - switch (period.toLowerCase()) { - case "day": - days = 1; - break; - case "month": - days = 30; - break; - case "week": - default: - days = 7; - break; - } + int days = + switch (period.toLowerCase()) { + case "day" -> 1; + case "month" -> 30; + default -> 7; + }; // Get events from the specified period and previous period Instant now = Instant.now(); @@ -754,7 +740,7 @@ public class AuditRestController { List> files = (List>) eventData.get("files"); if (files != null && !files.isEmpty()) { - Map firstFile = files.get(0); + Map firstFile = files.getFirst(); data.put("documentname", String.valueOf(firstFile.getOrDefault("name", ""))); data.put("author", String.valueOf(firstFile.getOrDefault("pdfAuthor", ""))); data.put("filehash", String.valueOf(firstFile.getOrDefault("fileHash", ""))); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/policy/engine/PolicyExecutor.java b/app/proprietary/src/main/java/stirling/software/proprietary/policy/engine/PolicyExecutor.java index 06ac1c8d7c..8a36700460 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/policy/engine/PolicyExecutor.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/policy/engine/PolicyExecutor.java @@ -131,7 +131,7 @@ public class PolicyExecutor { // One call over all inputs. The outputs derive from a single input only when exactly // one entered; otherwise (a genuine merge) there is no single source. ToolResult r = callEndpoint(step, inputFiles, supportingFiles); - Integer origin = inputOrigins.size() == 1 ? inputOrigins.get(0) : null; + Integer origin = inputOrigins.size() == 1 ? inputOrigins.getFirst() : null; for (Resource file : r.files()) { files.add(file); origins.add(origin); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/CacheConfig.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/CacheConfig.java index eef27703f2..36dcaecfd2 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/CacheConfig.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/CacheConfig.java @@ -2,7 +2,6 @@ package stirling.software.proprietary.security.configuration; import java.time.Duration; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; @@ -19,7 +18,6 @@ public class CacheConfig { private final ApplicationProperties applicationProperties; - @Autowired public CacheConfig(ApplicationProperties applicationProperties) { this.applicationProperties = applicationProperties; } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2ResponseAuthenticationConverter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2ResponseAuthenticationConverter.java index 0acc98ee55..b2ce4adb68 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2ResponseAuthenticationConverter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2ResponseAuthenticationConverter.java @@ -61,7 +61,7 @@ public class CustomSaml2ResponseAuthenticationConverter @Override public Saml2Authentication convert(ResponseToken responseToken) { - Assertion assertion = responseToken.getResponse().getAssertions().get(0); + Assertion assertion = responseToken.getResponse().getAssertions().getFirst(); Map> attributes = extractAttributes(assertion); // Debug log with actual values @@ -117,6 +117,6 @@ public class CustomSaml2ResponseAuthenticationConverter private String getFirstAttributeValue(Map> attributes, String name) { List values = attributes.get(name); - return values != null && !values.isEmpty() ? values.get(0).toString() : null; + return values != null && !values.isEmpty() ? values.getFirst().toString() : null; } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java index f18f6bd160..2c074c0a6b 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java @@ -232,7 +232,7 @@ public class DatabaseService implements DatabaseServiceInterface { List backupList = this.getBackupList(); backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed()); - Path latestExport = Path.of(backupList.get(0).getFilePath()); + Path latestExport = Path.of(backupList.getFirst().getFilePath()); executeDatabaseScript(latestExport); } @@ -376,7 +376,7 @@ public class DatabaseService implements DatabaseServiceInterface { List backupList = this.getBackupList(); List> deletedFiles = new ArrayList<>(); if (!backupList.isEmpty()) { - FileInfo lastBackup = backupList.get(backupList.size() - 1); + FileInfo lastBackup = backupList.getLast(); try { Files.deleteIfExists(Path.of(lastBackup.getFilePath())); deletedFiles.add(Pair.of(lastBackup, true)); @@ -399,7 +399,7 @@ public class DatabaseService implements DatabaseServiceInterface { Comparator.comparing( p -> p.getFileName().substring(7, p.getFileName().length() - 4))); - FileInfo oldestFile = filteredBackupList.get(0); + FileInfo oldestFile = filteredBackupList.getFirst(); Files.deleteIfExists(Path.of(oldestFile.getFilePath())); log.info("Deleted oldest backup: {}", oldestFile.getFileName()); } catch (IOException e) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/session/SessionPersistentRegistry.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/session/SessionPersistentRegistry.java index 19adb9a970..e615416e59 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/session/SessionPersistentRegistry.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/session/SessionPersistentRegistry.java @@ -193,6 +193,6 @@ public class SessionPersistentRegistry implements SessionRegistry { allSessions.sort((s1, s2) -> s2.getLastRequest().compareTo(s1.getLastRequest())); // The first session in the list is the latest session for the given principal name - return Optional.of(allSessions.get(0)); + return Optional.of(allSessions.getFirst()); } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/service/PortalInfraAuditService.java b/app/proprietary/src/main/java/stirling/software/proprietary/service/PortalInfraAuditService.java index 86f944ca9d..9a76cdd25b 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/service/PortalInfraAuditService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/service/PortalInfraAuditService.java @@ -299,7 +299,7 @@ public class PortalInfraAuditService { Object files = data.get("files"); if (files instanceof List list && !list.isEmpty() - && list.get(0) instanceof Map f) { + && list.getFirst() instanceof Map f) { Object name = ((Map) f).get("name"); return name != null ? String.valueOf(name) : null; } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/workflow/service/UnifiedAccessControlService.java b/app/proprietary/src/main/java/stirling/software/proprietary/workflow/service/UnifiedAccessControlService.java index 57ad1c97e7..dd3fcb9cc3 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/workflow/service/UnifiedAccessControlService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/workflow/service/UnifiedAccessControlService.java @@ -119,20 +119,18 @@ public class UnifiedAccessControlService { public ShareAccessRole getEffectiveRole(WorkflowParticipant participant) { ParticipantStatus status = participant.getStatus(); - switch (status) { - case SIGNED: - case DECLINED: - // After action completed, downgrade to read-only - return ShareAccessRole.VIEWER; - case PENDING: - case NOTIFIED: - case VIEWED: - // Active participants retain their assigned role - return participant.getAccessRole(); - default: + return switch (status) { + case SIGNED, DECLINED -> + // After action completed, downgrade to read-only + ShareAccessRole.VIEWER; + case PENDING, NOTIFIED, VIEWED -> + // Active participants retain their assigned role + participant.getAccessRole(); + default -> { log.warn("Unknown participant status: {}", status); - return ShareAccessRole.VIEWER; - } + yield ShareAccessRole.VIEWER; + } + }; } /** Checks if a user can access a specific file */ diff --git a/app/saas/src/main/java/stirling/software/saas/accountlink/AccountLinkController.java b/app/saas/src/main/java/stirling/software/saas/accountlink/AccountLinkController.java index 490b3d5db1..646791f76e 100644 --- a/app/saas/src/main/java/stirling/software/saas/accountlink/AccountLinkController.java +++ b/app/saas/src/main/java/stirling/software/saas/accountlink/AccountLinkController.java @@ -152,7 +152,7 @@ public class AccountLinkController { if (rows.isEmpty()) { return new LeaderTeam(null, null, HttpStatus.FORBIDDEN); } - TeamMembership m = rows.get(0); + TeamMembership m = rows.getFirst(); if (m.getRole() != TeamRole.LEADER) { return new LeaderTeam(null, null, HttpStatus.FORBIDDEN); } diff --git a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygInvoicesController.java b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygInvoicesController.java index 80c20902ce..ad48a03743 100644 --- a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygInvoicesController.java +++ b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygInvoicesController.java @@ -105,7 +105,7 @@ public class PaygInvoicesController { if (rows.isEmpty()) { return ResponseEntity.ok(List.of()); } - Long teamId = rows.get(0).getTeam().getId(); + Long teamId = rows.getFirst().getTeam().getId(); // No PAYG extension row OR no Stripe customer id → team has never subscribed → no // invoices. Empty list, not 404 — the UI distinguishes "no invoices yet" from a diff --git a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygPaymentMethodController.java b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygPaymentMethodController.java index 89ab18230f..22a84eb489 100644 --- a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygPaymentMethodController.java +++ b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygPaymentMethodController.java @@ -85,7 +85,7 @@ public class PaygPaymentMethodController { if (rows.isEmpty()) { return ResponseEntity.ok(PaymentMethodResponse.absent()); } - Long teamId = rows.get(0).getTeam().getId(); + Long teamId = rows.getFirst().getTeam().getId(); Optional ext = extRepo.findById(teamId); if (ext.isEmpty() || ext.get().getStripeCustomerId() == null) { diff --git a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygWalletController.java b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygWalletController.java index 0f18f13284..ffc4a056fc 100644 --- a/app/saas/src/main/java/stirling/software/saas/payg/api/PaygWalletController.java +++ b/app/saas/src/main/java/stirling/software/saas/payg/api/PaygWalletController.java @@ -427,7 +427,7 @@ public class PaygWalletController { private Optional primaryMembership(Long userId) { List rows = memberRepo.findPrimaryMembership(userId); - return rows.isEmpty() ? Optional.empty() : Optional.of(rows.get(0)); + return rows.isEmpty() ? Optional.empty() : Optional.of(rows.getFirst()); } private List buildMemberRows( diff --git a/app/saas/src/main/java/stirling/software/saas/payg/charge/JobChargeService.java b/app/saas/src/main/java/stirling/software/saas/payg/charge/JobChargeService.java index 2fc175f224..1310d2ea97 100644 --- a/app/saas/src/main/java/stirling/software/saas/payg/charge/JobChargeService.java +++ b/app/saas/src/main/java/stirling/software/saas/payg/charge/JobChargeService.java @@ -324,7 +324,7 @@ public class JobChargeService { List paths = inputs.stream().map(JobInput::path).toList(); DocumentMetrics metrics = multiparts.size() == 1 - ? classifier.classify(multiparts.get(0), paths.get(0), policy) + ? classifier.classify(multiparts.getFirst(), paths.getFirst(), policy) : classifier.classify(multiparts, paths, policy); // Apply the policy-level minChargeUnits floor per design § 3.4. The classifier returns // raw docUnits with a "non-empty input → ≥1" floor; the charge formula's diff --git a/app/saas/src/main/java/stirling/software/saas/payg/lineage/JpaJobLineageStore.java b/app/saas/src/main/java/stirling/software/saas/payg/lineage/JpaJobLineageStore.java index 09df6ed12f..2a92d941bf 100644 --- a/app/saas/src/main/java/stirling/software/saas/payg/lineage/JpaJobLineageStore.java +++ b/app/saas/src/main/java/stirling/software/saas/payg/lineage/JpaJobLineageStore.java @@ -75,7 +75,7 @@ public class JpaJobLineageStore implements JobLineageStore { List matches = hashRepository.findOpenJobsForSignatures( userId, JobStatus.OPEN, since, storageKeys, Limit.of(1)); - return matches.isEmpty() ? Optional.empty() : Optional.of(matches.get(0)); + return matches.isEmpty() ? Optional.empty() : Optional.of(matches.getFirst()); } @Override diff --git a/app/saas/src/main/java/stirling/software/saas/security/SupabaseAuthenticationFilter.java b/app/saas/src/main/java/stirling/software/saas/security/SupabaseAuthenticationFilter.java index a4b2366379..162a5fe408 100644 --- a/app/saas/src/main/java/stirling/software/saas/security/SupabaseAuthenticationFilter.java +++ b/app/saas/src/main/java/stirling/software/saas/security/SupabaseAuthenticationFilter.java @@ -291,7 +291,7 @@ public class SupabaseAuthenticationFilter extends OncePerRequestFilter { if (!(raw instanceof List amrList) || amrList.isEmpty()) { return WEB; } - Object first = amrList.get(0); + Object first = amrList.getFirst(); if (!(first instanceof Map entry)) { return WEB; }