mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
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 <Ludy87@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<List<Line>> anchorGroups = new ArrayList<>();
|
||||
List<Line> 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<float[]> 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<List<Line>> merged = new ArrayList<>(prev.rows());
|
||||
List<List<Line>> 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -114,7 +114,7 @@ public enum FormFieldTypeSupport {
|
||||
return;
|
||||
}
|
||||
|
||||
PDAnnotationWidget widget = checkBox.getWidgets().get(0);
|
||||
PDAnnotationWidget widget = checkBox.getWidgets().getFirst();
|
||||
|
||||
PDAppearanceCharacteristicsDictionary appearanceChars =
|
||||
widget.getAppearanceCharacteristics();
|
||||
|
||||
@@ -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<FormFieldInfo> 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<String> 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<String> 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<String> 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<String> resolveOptions(PDTerminalField field) {
|
||||
try {
|
||||
if (field instanceof PDChoice choice) {
|
||||
LinkedHashSet<String> allowed = new LinkedHashSet<>();
|
||||
List<String> exportValues = choice.getOptionsExportValues();
|
||||
List<String> displayValues = choice.getOptionsDisplayValues();
|
||||
return switch (field) {
|
||||
case PDChoice choice -> {
|
||||
LinkedHashSet<String> allowed = new LinkedHashSet<>();
|
||||
List<String> exportValues = choice.getOptionsExportValues();
|
||||
List<String> 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<String> exports = radio.getExportValues();
|
||||
yield exports != null && !exports.isEmpty()
|
||||
? new ArrayList<>(exports)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
return new ArrayList<>(allowed);
|
||||
} else if (field instanceof PDRadioButton radio) {
|
||||
List<String> exports = radio.getExportValues();
|
||||
if (exports != null && !exports.isEmpty()) {
|
||||
return new ArrayList<>(exports);
|
||||
case PDCheckBox checkBox -> {
|
||||
List<String> exports = checkBox.getExportValues();
|
||||
yield exports != null && !exports.isEmpty()
|
||||
? new ArrayList<>(exports)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
} else if (field instanceof PDCheckBox checkBox) {
|
||||
List<String> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -721,7 +721,7 @@ class PDFToFileTest {
|
||||
.thenAnswer(
|
||||
invocation -> {
|
||||
List<String> 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;
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
+8
-7
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+11
-20
@@ -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<String, Object> jsonContent =
|
||||
objectMapper.readValue(
|
||||
config, new TypeReference<Map<String, Object>>() {});
|
||||
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<String, String> 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 -> "";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+3
-3
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+2
-2
@@ -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<ImageReference> 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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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<PDField> acroFormFields = acroForm.getFields();
|
||||
acroForm.setSignaturesExist(true);
|
||||
acroForm.setAppendOnly(true);
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ public class RemoveCertSignController {
|
||||
// Remove signature fields safely
|
||||
List<PDField> fieldsToRemove =
|
||||
acroForm.getFields().stream()
|
||||
.filter(field -> field instanceof PDSignatureField)
|
||||
.filter(PDSignatureField.class::isInstance)
|
||||
.toList();
|
||||
|
||||
if (!fieldsToRemove.isEmpty()) {
|
||||
|
||||
+5
-2
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -137,7 +137,7 @@ public class JobController {
|
||||
if (result.hasFiles() && !result.hasMultipleFiles()) {
|
||||
try {
|
||||
List<ResultFile> files = result.getAllResultFiles();
|
||||
ResultFile singleFile = files.get(0);
|
||||
ResultFile singleFile = files.getFirst();
|
||||
|
||||
byte[] fileContent = fileStorage.retrieveBytes(singleFile.getFileId());
|
||||
return ResponseEntity.ok()
|
||||
|
||||
+1
-6
@@ -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
|
||||
|
||||
-58
@@ -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
|
||||
|
||||
+66
@@ -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<UIDataController.PipelineData> response = controller.getPipelineData();
|
||||
|
||||
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
|
||||
UIDataController.PipelineData body = response.getBody();
|
||||
assertThat(body).isNotNull();
|
||||
|
||||
List<Map<String, String>> 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);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -204,7 +204,7 @@ class ConvertOfficeControllerTest {
|
||||
inv -> {
|
||||
// unoconvert writes directly to the output path (last arg)
|
||||
List<String> 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 <basename>.pdf into the --outdir (workDir)
|
||||
List<String> 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<String> 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<String> 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<String> 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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ class CompressControllerMoreTest {
|
||||
|
||||
// The qpdf output path is the last argument of the command.
|
||||
private static Path qpdfOutputPath(List<String> 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. */
|
||||
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
|
||||
+6
-10
@@ -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<String> 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<String> cmd = inv.getArgument(0);
|
||||
writeValidPdfTo(cmd, 2);
|
||||
writeValidPdfTo(Path.of(cmd.get(2)));
|
||||
return okResult;
|
||||
});
|
||||
|
||||
@@ -176,7 +172,7 @@ class RepairControllerMoreTest {
|
||||
.thenAnswer(
|
||||
inv -> {
|
||||
List<String> 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<String> 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<String> cmd = inv.getArgument(0);
|
||||
writeValidPdfTo(cmd, cmd.size() - 1);
|
||||
writeValidPdfTo(Path.of(cmd.getLast()));
|
||||
return okResult;
|
||||
});
|
||||
|
||||
|
||||
+1
-9
@@ -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}")
|
||||
|
||||
+13
-27
@@ -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<Map<String, Object>> files =
|
||||
(List<Map<String, Object>>) eventData.get("files");
|
||||
if (files != null && !files.isEmpty()) {
|
||||
Map<String, Object> firstFile = files.get(0);
|
||||
Map<String, Object> 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", "")));
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
-2
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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<String, List<Object>> attributes = extractAttributes(assertion);
|
||||
|
||||
// Debug log with actual values
|
||||
@@ -117,6 +117,6 @@ public class CustomSaml2ResponseAuthenticationConverter
|
||||
|
||||
private String getFirstAttributeValue(Map<String, List<Object>> attributes, String name) {
|
||||
List<Object> values = attributes.get(name);
|
||||
return values != null && !values.isEmpty() ? values.get(0).toString() : null;
|
||||
return values != null && !values.isEmpty() ? values.getFirst().toString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -232,7 +232,7 @@ public class DatabaseService implements DatabaseServiceInterface {
|
||||
List<FileInfo> 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<FileInfo> backupList = this.getBackupList();
|
||||
List<Pair<FileInfo, Boolean>> 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) {
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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<String, Object>) f).get("name");
|
||||
return name != null ? String.valueOf(name) : null;
|
||||
}
|
||||
|
||||
+11
-13
@@ -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 */
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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<PaygTeamExtensions> ext = extRepo.findById(teamId);
|
||||
if (ext.isEmpty() || ext.get().getStripeCustomerId() == null) {
|
||||
|
||||
@@ -427,7 +427,7 @@ public class PaygWalletController {
|
||||
|
||||
private Optional<TeamMembership> primaryMembership(Long userId) {
|
||||
List<TeamMembership> 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<MemberRow> buildMemberRows(
|
||||
|
||||
@@ -324,7 +324,7 @@ public class JobChargeService {
|
||||
List<Path> 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
|
||||
|
||||
@@ -75,7 +75,7 @@ public class JpaJobLineageStore implements JobLineageStore {
|
||||
List<LineageMatch> 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
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user