diff --git a/utility-db/src/main/java/com/dua3/utility/db/DbUtil.java b/utility-db/src/main/java/com/dua3/utility/db/DbUtil.java index 5e9db532..0685127a 100644 --- a/utility-db/src/main/java/com/dua3/utility/db/DbUtil.java +++ b/utility-db/src/main/java/com/dua3/utility/db/DbUtil.java @@ -65,8 +65,9 @@ public final class DbUtil { try { // load properties Map p; - try (InputStream in = DbUtil.class.getResourceAsStream("jdbc_drivers.properties")) { - p = LangUtil.loadProperties(in); + String resource = "jdbc_drivers.properties"; + try (InputStream in = DbUtil.class.getResourceAsStream(resource)) { + p = LangUtil.loadProperties(Objects.requireNonNull(in, "resource nott found: " + resource)); } // parse entries @@ -287,8 +288,7 @@ public static DataSource createDataSource(Driver driver, String url, String user throws SQLException { LangUtil.check(driver.acceptsURL(url), () -> new SQLException("URL not accepted by driver")); - JdbcDataSource ds = new JdbcDataSource(); - ds.setDriver(driver); + JdbcDataSource ds = new JdbcDataSource(driver); ds.setUrl(url); ds.setUser(user); ds.setPassword(password); diff --git a/utility-db/src/main/java/com/dua3/utility/db/JdbcDataSource.java b/utility-db/src/main/java/com/dua3/utility/db/JdbcDataSource.java index d5aeb02c..e4304bfd 100644 --- a/utility-db/src/main/java/com/dua3/utility/db/JdbcDataSource.java +++ b/utility-db/src/main/java/com/dua3/utility/db/JdbcDataSource.java @@ -28,13 +28,15 @@ public class JdbcDataSource implements DataSource { private @Nullable String url; private @Nullable PrintWriter logWriter; private int loginTimeout; - private @Nullable Driver driver; + private Driver driver; /** * Constructor. + * + * @param driver the driver */ - public JdbcDataSource() { - // nop + public JdbcDataSource(Driver driver) { + this.driver = driver; } private void log(String message) { @@ -43,15 +45,6 @@ private void log(String message) { } } - /** - * Set the JDBC driver for this instance. - * - * @param driver the driver - */ - public void setDriver(Driver driver) { - this.driver = driver; - } - @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException("getParentLogger() is not supported"); diff --git a/utility-db/src/main/java/com/dua3/utility/db/NamedParameterStatement.java b/utility-db/src/main/java/com/dua3/utility/db/NamedParameterStatement.java index fcd20a33..82373c88 100644 --- a/utility-db/src/main/java/com/dua3/utility/db/NamedParameterStatement.java +++ b/utility-db/src/main/java/com/dua3/utility/db/NamedParameterStatement.java @@ -121,7 +121,7 @@ public NamedParameterStatement(Connection connection, String query) throws SQLEx statement = connection.prepareStatement(parsedQuery); } - private static JDBCType getParameterType(ParameterMetaData meta, int index) { + private static @Nullable JDBCType getParameterType(ParameterMetaData meta, int index) { try { return JDBCType.valueOf(meta.getParameterType(index)); } catch (SQLException e) { @@ -281,7 +281,7 @@ private void setNonNullWithLongArg(String name, T value, long arg, SetParame } } - private void setWithObjectArg(SQLType type, String name, @Nullable T value, @Nullable U arg, SetParameterObject setter) throws SQLException { + private void setWithObjectArg(SQLType type, String name, @Nullable T value, @Nullable U arg, SetParameterObject setter) throws SQLException { if (value == null) { setNull(name, type); } else { @@ -1152,7 +1152,7 @@ private interface SetParameterObject { public static class ParameterInfo { final String name; final List indexes = new ArrayList<>(); - JDBCType type; + @Nullable JDBCType type; ParameterInfo(String name) { this.name = name; diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AbstractDialogBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AbstractDialogBuilder.java index 03b193cf..94d0439d 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AbstractDialogBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AbstractDialogBuilder.java @@ -34,7 +34,7 @@ public abstract class AbstractDialogBuilder, B exte extends AbstractDialogPaneBuilder { private final BiConsumer titleSetter; - private @Nullable final Window parentWindow; + private final @Nullable Window parentWindow; private @Nullable String title; protected AbstractDialogBuilder(@Nullable Window parentWindow) { diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AlertBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AlertBuilder.java index fbc4c647..a6dcd790 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AlertBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/AlertBuilder.java @@ -36,7 +36,7 @@ public class AlertBuilder private ButtonType @Nullable[] buttons; private @Nullable ButtonType defaultButton; - AlertBuilder(AlertType type, Window parentWindow) { + AlertBuilder(AlertType type, @Nullable Window parentWindow) { super(parentWindow); setDialogSupplier(() -> new Alert(type)); } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ChoiceInputControl.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ChoiceInputControl.java index 0d7f9d2f..169ef6eb 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ChoiceInputControl.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ChoiceInputControl.java @@ -9,6 +9,7 @@ import javafx.beans.property.SimpleStringProperty; import javafx.scene.Node; import javafx.scene.control.ComboBox; +import org.jspecify.annotations.Nullable; import java.util.function.Supplier; @@ -17,7 +18,7 @@ * * @param the input result type */ -public class ChoiceInputControl implements InputControl { +public class ChoiceInputControl implements InputControl { private final ComboBox> control; private final ChoiceOption option; diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ComboBoxEx.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ComboBoxEx.java index 0790e573..28768b05 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ComboBoxEx.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/ComboBoxEx.java @@ -1,5 +1,7 @@ package com.dua3.utility.fx.controls; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; import org.jspecify.annotations.Nullable; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; @@ -36,7 +38,7 @@ * * @param the type of the items contained in the ComboBox */ -public class ComboBoxEx extends CustomControl implements InputControl { +public class ComboBoxEx extends CustomControl implements InputControl { private static final Logger LOG = LogManager.getLogger(ComboBoxEx.class); private @Nullable Comparator comparator = null; @@ -143,6 +145,11 @@ protected void updateItem(@Nullable T item, boolean empty) { } private void editItem() { + if (edit == null) { + LOG.warn("editing not supported"); + return; + } + int idx = comboBox.getSelectionModel().getSelectedIndex(); if (idx >= 0) { T item = items.get(idx); @@ -157,7 +164,7 @@ private void editItem() { } private void addItem() { - Optional.ofNullable(add.get()).ifPresent(item -> { + Optional.ofNullable(add).map(Supplier::get).ifPresent(item -> { items.add(item); comboBox.getSelectionModel().select(item); sortItems(); @@ -280,11 +287,11 @@ public void reset() { @Override public ReadOnlyBooleanProperty validProperty() { - return null; // FIXME + return new SimpleBooleanProperty(true); } @Override public ReadOnlyStringProperty errorProperty() { - return null; // FIXME + return new SimpleStringProperty(""); } } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileChooserBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileChooserBuilder.java index de205d1a..b5452c01 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileChooserBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileChooserBuilder.java @@ -43,7 +43,7 @@ public class FileChooserBuilder { private Path initialDir = USER_HOME; private String initialFileName = ""; private List filters = new ArrayList<>(); - private ExtensionFilter selectedFilter = null; + private @Nullable ExtensionFilter selectedFilter = null; FileChooserBuilder() { } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileInput.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileInput.java index 969591d6..11bce8f9 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileInput.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/FileInput.java @@ -44,7 +44,7 @@ * These properties are updated based on the path selected by the user and the * specified validation function.

*/ -public class FileInput extends CustomControl implements InputControl { +public class FileInput extends CustomControl implements InputControl<@Nullable Path> { private static final StringConverter PATH_CONVERTER = new PathConverter(); @@ -60,11 +60,11 @@ public Path fromString(@Nullable String s) { } } - private final ObjectProperty value = new SimpleObjectProperty<>(); + private final ObjectProperty<@Nullable Path> value = new SimpleObjectProperty<>(); private final FileDialogMode mode; private final FileChooser.ExtensionFilter[] filters; - private final Supplier dflt; + private final Supplier<@Nullable Path> dflt; private final StringProperty error = new SimpleStringProperty(""); private final BooleanProperty valid = new SimpleBooleanProperty(true); @@ -83,7 +83,7 @@ public FileInput( boolean existingOnly, Supplier dflt, Collection filters, - Function> validate) { + Function<@Nullable Path, Optional> validate) { super(new HBox()); getStyleClass().setAll("file-input"); @@ -208,7 +208,7 @@ public static Function> defaultValidate(FileDialogMode mo }; } - private Path getPath() { + private @Nullable Path getPath() { return value.get(); } @@ -223,7 +223,7 @@ public void reset() { } @Override - public Property valueProperty() { + public Property<@Nullable Path> valueProperty() { return value; } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputBuilder.java index 8fa8e36a..94c56691 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputBuilder.java @@ -286,7 +286,7 @@ default B comboBoxEx( @Nullable Supplier add, @Nullable BiPredicate, T> remove, Function format, - Supplier dflt, + Supplier<@Nullable T> dflt, Class cls, Collection items ) { @@ -333,7 +333,7 @@ B comboBoxEx( * @param items the items to choose from * @return {@code this} */ - default B radioList( + default B radioList( String id, String label, Supplier dflt, diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGrid.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGrid.java index 152dca7a..7c748a1b 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGrid.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGrid.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,7 +42,7 @@ public class InputGrid extends GridPane { private static final String MARKER_OK = ""; protected final BooleanProperty valid = new SimpleBooleanProperty(false); - private Collection> data = null; + private Collection> data = Collections.emptyList(); private int columns = 1; /** @@ -166,12 +167,12 @@ public void reset() { * * @param the input's value type */ - static final class Meta { + static final class Meta { final String id; final Class cls; final Supplier dflt; final InputControl control; - final Label label; + final @Nullable Label label; final Label marker = new Label(); Meta(String id, @Nullable String label, Class cls, Supplier dflt, InputControl control) { diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGridBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGridBuilder.java index fd12afa3..e9c5b54c 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGridBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/InputGridBuilder.java @@ -74,7 +74,7 @@ static class ControlWrapper implements InputControl { private final Node node; - private final Property value = new SimpleObjectProperty<>(null); + private final Property value = new SimpleObjectProperty<>(); private final BooleanProperty valid = new SimpleBooleanProperty(true); private final ReadOnlyStringProperty error = new SimpleStringProperty(""); @@ -120,7 +120,7 @@ private InputGridBuilder doAdd(String id, @Nullable String label, Class t @Override public InputGridBuilder addNode(String id, @Nullable String label, Node node) { - Meta meta = new Meta<>(id, label, Void.class, null, new ControlWrapper(node)); + Meta meta = new Meta<>(id, label, Void.class, () -> null, new ControlWrapper(node)); Meta prev = data.put(id, meta); LangUtil.check(prev == null, "Input with id '" + id + "' already defined"); return this; @@ -128,7 +128,7 @@ public InputGridBuilder addNode(String id, @Nullable String label, Node node) { @Override public InputGridBuilder addNode(String id, Node node) { - Meta meta = new Meta<>(id, null, Void.class, null, new ControlWrapper(node)); + Meta meta = new Meta<>(id, null, Void.class, () -> null, new ControlWrapper(node)); Meta prev = data.put(id, meta); LangUtil.check(prev == null, "Input with id '" + id + "' already defined"); return this; diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/OptionsPane.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/OptionsPane.java index 556b779b..246a8da0 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/OptionsPane.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/OptionsPane.java @@ -43,9 +43,9 @@ public class OptionsPane extends GridPane implements InputControl { */ protected static final Logger LOG = LogManager.getLogger(OptionsPane.class); private static final Insets INSETS = new Insets(2); - private final InputControl.State state; + private final InputControl.State<@Nullable Arguments> state; private final Supplier>> options; - private final Supplier dflt; + private final Supplier<@Nullable Arguments> dflt; private final Map, InputControl> items = new LinkedHashMap<>(); /** @@ -71,7 +71,7 @@ public OptionsPane(Collection> optionSet, Arguments currentValues) { public OptionsPane(Supplier>> options, Supplier dflt) { this.options = options; this.dflt = dflt; - Property value = new SimpleObjectProperty<>(); + Property<@Nullable Arguments> value = new SimpleObjectProperty<>(); this.state = new State<>(value, dflt); } @@ -130,7 +130,7 @@ public void init() { } @SuppressWarnings("unchecked") - private InputControl createControl(Arguments values, Option option) { + private InputControl createControl(Arguments values, Option option) { if (option instanceof ChoiceOption co) { return new ChoiceInputControl<>(co, supplyDefault(co, values)); } else if (option instanceof Flag f) { @@ -189,7 +189,7 @@ public void reset() { } @Override - public Property valueProperty() { + public Property<@Nullable Arguments> valueProperty() { return state.valueProperty(); } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/RadioPane.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/RadioPane.java index 44ba9dab..1976ec58 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/RadioPane.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/RadioPane.java @@ -28,7 +28,7 @@ * * @param The type of items that will be represented as radio buttons. */ -public class RadioPane extends VBox implements InputControl { +public class RadioPane extends VBox implements InputControl { protected static final Logger LOG = LogManager.getLogger(RadioPane.class); private static final double SPACING = 4; @@ -58,7 +58,7 @@ public RadioPane(Collection items, @Nullable T currentValue, Function property = new SimpleObjectProperty<>(); + Property<@Nullable T> property = new SimpleObjectProperty<>(); group.selectedToggleProperty().addListener((v, o, n) -> { Toggle toggle = group.getSelectedToggle(); property.setValue(toggle != null ? (T) toggle.getUserData() : null); diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SimpleInputControl.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SimpleInputControl.java index e7512f69..8dfdebb5 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SimpleInputControl.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SimpleInputControl.java @@ -5,6 +5,7 @@ import javafx.beans.property.ReadOnlyStringProperty; import javafx.scene.control.Control; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.util.Optional; import java.util.function.Function; @@ -17,7 +18,7 @@ * @param the type of the control, which must extend from Control * @param the type of the value held by the input control */ -public class SimpleInputControl implements InputControl { +public class SimpleInputControl implements InputControl { private final C control; private final State state; diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SliderBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SliderBuilder.java index b7589419..0d9169ca 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SliderBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/SliderBuilder.java @@ -4,6 +4,7 @@ import javafx.beans.value.ObservableNumberValue; import javafx.geometry.Orientation; import javafx.scene.Node; +import org.jspecify.annotations.Nullable; import java.util.function.BiFunction; import java.util.function.DoubleConsumer; @@ -168,7 +169,7 @@ public SliderBuilder bind(ObservableNumberValue value) { * @param value the property to be bound bidirectionally with the slider's value property. * @return this instance of {@code SliderBuilder} for method chaining. */ - public SliderBuilder bindBidirectional(Property value) { + public SliderBuilder bindBidirectional(Property<@Nullable Number> value) { slider.valueProperty().bindBidirectional(value); return this; } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TableCellAutoCommit.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TableCellAutoCommit.java index 22df2feb..d60fba34 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TableCellAutoCommit.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TableCellAutoCommit.java @@ -134,13 +134,14 @@ public void commitEdit(@Nullable T newValue) { TableView table = getTableView(); if (table != null) { TableColumn column = getTableColumn(); - CellEditEvent event = new CellEditEvent<>( + @SuppressWarnings("DataFlowIssue") CellEditEvent event = new CellEditEvent<>( table, new TablePosition<>(table, getIndex(), column), TableColumn.editCommitEvent(), newValue ); Event.fireEvent(column, event); Platform.runLater(table::refresh); } } + //noinspection DataFlowIssue super.commitEdit(newValue); setContentDisplay(ContentDisplay.TEXT_ONLY); } diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TextFieldBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TextFieldBuilder.java index 3340083d..14e3aeba 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TextFieldBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/TextFieldBuilder.java @@ -19,14 +19,16 @@ public final class TextFieldBuilder { private static final Pattern INTEGER_PATTERN = Pattern.compile("\\d*|0"); - private static final UnaryOperator INTEGER_FILTER = change -> INTEGER_PATTERN.matcher(change.getControlNewText()).matches() ? change : null; + @SuppressWarnings("DataFlowIssue") // should be Function but type is incompatible + private static final UnaryOperator INTEGER_FILTER = change -> INTEGER_PATTERN.matcher(change.getControlNewText()).matches() ? change : null; private static TextFormatter getIntegerTextFormatter(UnaryOperator integerFilter) { return new TextFormatter<>(new IntegerStringConverter(), 0, integerFilter); } private static final Pattern SIGNED_INTEGER_PATTERN = Pattern.compile("-?([1-9]\\d*|0)?"); - private static final UnaryOperator SIGNED_INTEGER_FILTER = change -> SIGNED_INTEGER_PATTERN.matcher(change.getControlNewText()).matches() ? change : null; + @SuppressWarnings("DataFlowIssue") // should be Function but type is incompatible + private static final UnaryOperator SIGNED_INTEGER_FILTER = change -> SIGNED_INTEGER_PATTERN.matcher(change.getControlNewText()).matches() ? change : null; private @Nullable String text; private TextFieldType type = TextFieldType.TEXT; diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/Validator.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/Validator.java index 69a72531..69c63788 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/Validator.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/Validator.java @@ -58,7 +58,7 @@ public class Validator { private static final Logger LOG = LogManager.getLogger(Validator.class); - private final ResourceBundle resources; + private final @Nullable ResourceBundle resources; private final LinkedHashMap>> controls = new LinkedHashMap<>(); private final MapProperty validationResultProperty = new SimpleMapProperty<>(); private final BooleanProperty validProperty = new SimpleBooleanProperty(); diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialog.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialog.java index 47c8ca05..6ec3d9ff 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialog.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialog.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -106,6 +108,10 @@ public void setPages(Map> pages, String startPage) { * @throws IllegalStateException if any page refers to a 'next' page that does not exist */ private void checkPages() { + if (pages == null) { + return; + } + Set pageNames = pages.keySet(); for (Entry> entry : pages.entrySet()) { String name = entry.getKey(); @@ -153,7 +159,7 @@ private void checkPages() { } private void setPage(String pageName) { - this.current = Pair.of(pageName, pages.get(pageName)); + this.current = Pair.of(pageName, Objects.requireNonNull(pages, "pages not set").get(pageName)); InputDialogPane pane = current.second().pane; setDialogPane(pane); @@ -217,8 +223,8 @@ public boolean isShowPreviousButton() { * * @return the current wizard page as a {@link Page} object */ - public Page getCurrentPage() { - return current.second(); + public Optional> getCurrentPage() { + return Optional.ofNullable(current).map(Pair::second); } /** diff --git a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialogBuilder.java b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialogBuilder.java index aa5d5785..954c6535 100644 --- a/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialogBuilder.java +++ b/utility-fx-controls/src/main/java/com/dua3/utility/fx/controls/WizardDialogBuilder.java @@ -1,5 +1,7 @@ package com.dua3.utility.fx.controls; +import org.jspecify.annotations.Nullable; + import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; @@ -13,7 +15,7 @@ public class WizardDialogBuilder { final LinkedHashMap> pages = new LinkedHashMap<>(); private String title = ""; - private String startPage = null; + private String startPage = ""; WizardDialogBuilder() {} @@ -45,7 +47,7 @@ public , B extends AbstractPaneBuilder, R> page.setNext(builder.next); pages.put(name, page); - if (startPage == null) { + if (startPage.isEmpty()) { setStartPage(name); } diff --git a/utility-fx-db/src/main/java/com/dua3/utility/fx/db/FxDbUtil.java b/utility-fx-db/src/main/java/com/dua3/utility/fx/db/FxDbUtil.java index 8f7de1b9..7b0a1e4a 100644 --- a/utility-fx-db/src/main/java/com/dua3/utility/fx/db/FxDbUtil.java +++ b/utility-fx-db/src/main/java/com/dua3/utility/fx/db/FxDbUtil.java @@ -28,6 +28,8 @@ import javafx.util.Callback; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.sql.Clob; import java.sql.JDBCType; @@ -95,11 +97,14 @@ public static int fill(TableView> tv, ResultSet rs) throw int scale = meta.getScale(i); // define the formatting - Function format; + Function<@NonNull Object, @NonNull String> format; switch (sqlType) { - case DATE -> format = item -> DbUtil.toLocalDate(item).format(dateFormatter); - case TIMESTAMP -> format = item -> DbUtil.toLocalDateTime(item).format(timestampFormatter); - case TIME -> format = item -> DbUtil.toLocalDateTime(item).format(timeFormatter); + case DATE -> //noinspection DataFlowIssue - format is not called for null arguments + format = item -> DbUtil.toLocalDate(item).format(dateFormatter); + case TIMESTAMP -> //noinspection DataFlowIssue - format is not called for null arguments + format = item -> DbUtil.toLocalDateTime(item).format(timestampFormatter); + case TIME -> //noinspection DataFlowIssue - format is not called for null arguments + format = item -> DbUtil.toLocalDateTime(item).format(timeFormatter); // numbers that have scale case DECIMAL, NUMERIC -> { @@ -121,7 +126,7 @@ public static int fill(TableView> tv, ResultSet rs) throw LOG.trace("column name: {} label: {} type: {} scale: {}", name, label, sqlType, scale); // CellValueFactory - Callback, Object>, ObservableValue> cellValueFactory + Callback, Object>, ObservableValue<@Nullable Object>> cellValueFactory = param -> { var list = param.getValue(); var x = idx < list.size() ? list.get(idx) : null; diff --git a/utility-fx-icons-ikonli/src/main/java/com/dua3/utility/fx/icons/ikonli/IkonliIconProvider.java b/utility-fx-icons-ikonli/src/main/java/com/dua3/utility/fx/icons/ikonli/IkonliIconProvider.java index 1fc19805..e6754e9d 100644 --- a/utility-fx-icons-ikonli/src/main/java/com/dua3/utility/fx/icons/ikonli/IkonliIconProvider.java +++ b/utility-fx-icons-ikonli/src/main/java/com/dua3/utility/fx/icons/ikonli/IkonliIconProvider.java @@ -2,13 +2,16 @@ import com.dua3.utility.fx.icons.Icon; import com.dua3.utility.fx.icons.IconProvider; +import javafx.beans.property.IntegerProperty; import javafx.scene.Node; +import javafx.scene.paint.Paint; import org.kordamp.ikonli.Ikon; import org.kordamp.ikonli.IkonHandler; import org.kordamp.ikonli.javafx.FontIcon; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.Optional; import java.util.ServiceLoader; @@ -31,19 +34,20 @@ public String name() { } @Override - public Icon forName(String name) { + public Optional forName(String name) { for (var handler : ServiceLoader.load(IkonHandler.class)) { if (handler.supports(name)) { LOG.debug("using: {}", handler.getClass().getName()); var ikon = handler.resolve(name); - return new IkonliIcon(ikon, name); + return Optional.of(new IkonliIcon(ikon, name)); } } LOG.debug("icon not found: {}", name); - return null; + return Optional.empty(); } + @SuppressWarnings("NullableProblems") static class IkonliIcon extends FontIcon implements Icon { private final String name; @@ -52,6 +56,20 @@ static class IkonliIcon extends FontIcon implements Icon { this.name = name; } + @Override + public IntegerProperty iconSizeProperty() { + IntegerProperty p = super.iconSizeProperty(); + assert p != null; + return p; + } + + @Override + public Paint getIconColor() { + Paint c = super.getIconColor(); + assert c != null; + return c; + } + @Override public String getIconIdentifier() { return name; diff --git a/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconProvider.java b/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconProvider.java index 35f18a5d..8eb23519 100644 --- a/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconProvider.java +++ b/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconProvider.java @@ -1,5 +1,7 @@ package com.dua3.utility.fx.icons; +import java.util.Optional; + /** * The IconProvider interface represents a provider of icons. */ @@ -15,7 +17,7 @@ public interface IconProvider { * Get icon. * * @param name name of the requested icon - * @return icon or {@code null} if this provider does not offer the requested icon + * @return Optional holding the icon */ - Icon forName(String name); + Optional forName(String name); } diff --git a/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconUtil.java b/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconUtil.java index c416a96e..38986699 100644 --- a/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconUtil.java +++ b/utility-fx-icons/src/main/java/com/dua3/utility/fx/icons/IconUtil.java @@ -37,7 +37,7 @@ public static Optional iconFromName(String name) { return ServiceLoader.load(iconProviderClass) .stream() .peek(provider -> LOG.debug("found {} implementation: {}", iconProviderClass.getName(), provider.getClass().getName())) - .map(provider -> provider.get().forName(name)) + .map(provider -> provider.get().forName(name).orElse(null)) .filter(Objects::nonNull) .findFirst(); } diff --git a/utility-fx/src/main/java/com/dua3/utility/fx/FxLogPane.java b/utility-fx/src/main/java/com/dua3/utility/fx/FxLogPane.java index 316035ee..2954c0e2 100644 --- a/utility-fx/src/main/java/com/dua3/utility/fx/FxLogPane.java +++ b/utility-fx/src/main/java/com/dua3/utility/fx/FxLogPane.java @@ -58,7 +58,7 @@ public class FxLogPane extends BorderPane { private final TextArea details; private final TableView tableView; - private @Nullable volatile LogEntry selectedItem; + private volatile @Nullable LogEntry selectedItem; private boolean autoScroll = true; diff --git a/utility-fx/src/main/java/com/dua3/utility/fx/FxRefresh.java b/utility-fx/src/main/java/com/dua3/utility/fx/FxRefresh.java index 20e37fb2..c919ec02 100644 --- a/utility-fx/src/main/java/com/dua3/utility/fx/FxRefresh.java +++ b/utility-fx/src/main/java/com/dua3/utility/fx/FxRefresh.java @@ -60,10 +60,12 @@ public final class FxRefresh { private FxRefresh(String name, Runnable task) { this.name = name; this.task = task; - this.updateThread = new Thread(this::refreshLoop); - updateThread.setDaemon(true); - updateThread.start(); + Thread thread = new Thread(this::refreshLoop); + thread.setDaemon(true); + this.updateThread = thread; + + thread.start(); } /** diff --git a/utility-fx/src/main/java/com/dua3/utility/fx/FxUtil.java b/utility-fx/src/main/java/com/dua3/utility/fx/FxUtil.java index 9ec68613..ae8076a2 100644 --- a/utility-fx/src/main/java/com/dua3/utility/fx/FxUtil.java +++ b/utility-fx/src/main/java/com/dua3/utility/fx/FxUtil.java @@ -523,7 +523,7 @@ public void accept(@Nullable T t1, @Nullable T t2) { * @param the type of the value stored in the Value object * @return an ObservableValue object that reflects changes in the Value object */ - public static ObservableValue toObservableValue(Value value) { + public static ObservableValue toObservableValue(Value value) { return new ObservableValue<>() { @Override public void addListener(ChangeListener listener) { diff --git a/utility-fx/src/main/java/com/dua3/utility/fx/PlatformHelper.java b/utility-fx/src/main/java/com/dua3/utility/fx/PlatformHelper.java index 72b62726..220d5b33 100644 --- a/utility-fx/src/main/java/com/dua3/utility/fx/PlatformHelper.java +++ b/utility-fx/src/main/java/com/dua3/utility/fx/PlatformHelper.java @@ -38,6 +38,7 @@ private PlatformHelper() { public static void runAndWait(Runnable action) { runAndWait(() -> { action.run(); + //noinspection ReturnOfNull return null; }); } diff --git a/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogAppenderLog4j.java b/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogAppenderLog4j.java index bda4bbe8..59da6ff4 100644 --- a/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogAppenderLog4j.java +++ b/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogAppenderLog4j.java @@ -121,8 +121,8 @@ public static LogAppenderLog4j createAppender( @PluginElement("Filters") @Nullable Filter filter) { if (name == null) { - LOGGER.error("No name provided for {}", APPENDER_NAME); - return null; + LOGGER.warn("No name provided for {}", APPENDER_NAME); + name = "[unnamed]"; } if (layout == null) { layout = PatternLayout.createDefaultLayout(); diff --git a/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogEntryLog4J.java b/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogEntryLog4J.java index febeb640..504139e9 100644 --- a/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogEntryLog4J.java +++ b/utility-logging-log4j/src/main/java/com/dua3/utility/logging/log4j/LogEntryLog4J.java @@ -5,8 +5,10 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ReusableMessage; +import org.jspecify.annotations.Nullable; import java.time.Instant; +import java.util.Objects; import java.util.function.Supplier; /** @@ -17,8 +19,8 @@ public final class LogEntryLog4J implements LogEntry { private final Instant time; private final LogLevel level; private final String marker; - private Supplier messageFormatter; - private String formattedMessage; + private @Nullable Supplier messageFormatter; + private @Nullable String formattedMessage; private final Throwable throwable; /** @@ -58,7 +60,7 @@ public String message() { formattedMessage = messageFormatter.get(); messageFormatter = null; } - return formattedMessage; + return Objects.requireNonNullElse(formattedMessage, ""); } @Override diff --git a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LogEntrySlf4j.java b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LogEntrySlf4j.java index c86e2a64..94168bb3 100644 --- a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LogEntrySlf4j.java +++ b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LogEntrySlf4j.java @@ -8,6 +8,7 @@ import org.slf4j.spi.LocationAwareLogger; import java.time.Instant; +import java.util.Objects; import java.util.function.Supplier; /** @@ -18,9 +19,9 @@ public final class LogEntrySlf4j implements LogEntry { private final Instant time; private final LogLevel level; private final String marker; - private Supplier messageFormatter; + private @Nullable Supplier messageFormatter; private @Nullable String formattedMessage; - private final Throwable throwable; + private final @Nullable Throwable throwable; /** * Creates a new instance of LogEntrySlf4j. @@ -52,7 +53,7 @@ public String message() { formattedMessage = messageFormatter.get(); messageFormatter = null; } - return formattedMessage; + return Objects.requireNonNullElse(formattedMessage, ""); } @Override @@ -106,7 +107,7 @@ public String marker() { * @return the throwable associated with this log entry. */ @Override - public Throwable throwable() { + public @Nullable Throwable throwable() { return throwable; } diff --git a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerFactorySlf4j.java b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerFactorySlf4j.java index 06cf5abf..ce6a6a15 100644 --- a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerFactorySlf4j.java +++ b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerFactorySlf4j.java @@ -54,7 +54,7 @@ public class LoggerFactorySlf4j implements ILoggerFactory, LogEntryDispatcher { private final List> prefixes = new ArrayList<>(); private final List> handlers = new ArrayList<>(); - private final LogEntryHandler defaultHandler; + private final @Nullable LogEntryHandler defaultHandler; private volatile LogEntryFilter filter = LogEntryFilter.ALL_PASS_FILTER; /** @@ -183,7 +183,7 @@ public Collection getLogEntryHandlers() { * * @return the default log entry handler */ - public LogEntryHandler getDefaultHandler() { - return defaultHandler; + public Optional getDefaultHandler() { + return Optional.ofNullable(defaultHandler); } } diff --git a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerSlf4j.java b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerSlf4j.java index 5aa885d6..22b6fa66 100644 --- a/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerSlf4j.java +++ b/utility-logging-slf4j/src/main/java/com/dua3/utility/logging/slf4j/LoggerSlf4j.java @@ -56,7 +56,7 @@ public static void setDefaultLevel(Level level) { } @Override - protected String getFullyQualifiedCallerName() { + protected @Nullable String getFullyQualifiedCallerName() { return null; } diff --git a/utility-logging/src/main/java/com/dua3/utility/logging/LogEntry.java b/utility-logging/src/main/java/com/dua3/utility/logging/LogEntry.java index 9aa9f701..0eb18337 100644 --- a/utility-logging/src/main/java/com/dua3/utility/logging/LogEntry.java +++ b/utility-logging/src/main/java/com/dua3/utility/logging/LogEntry.java @@ -1,5 +1,7 @@ package com.dua3.utility.logging; +import org.jspecify.annotations.Nullable; + import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -51,7 +53,7 @@ public interface LogEntry { * * @return the throwable object associated with this LogEntry, or null if no throwable is present */ - Throwable throwable(); + @Nullable Throwable throwable(); /** * Formats the log entry with the given prefix and suffix. diff --git a/utility-logging/src/main/java/com/dua3/utility/logging/LogUtil.java b/utility-logging/src/main/java/com/dua3/utility/logging/LogUtil.java index dd8bab8d..38b175ec 100644 --- a/utility-logging/src/main/java/com/dua3/utility/logging/LogUtil.java +++ b/utility-logging/src/main/java/com/dua3/utility/logging/LogUtil.java @@ -16,7 +16,7 @@ private LogUtil() { private static final Logger LOG = LogManager.getLogger(LogUtil.class); - private @Nullable static LogEntryDispatcher globalDispatcher; + private static @Nullable LogEntryDispatcher globalDispatcher; private static synchronized void init() { if (globalDispatcher == null) { @@ -60,6 +60,7 @@ public static void assureInitialized() { */ public static LogEntryDispatcher getGlobalDispatcher() { assureInitialized(); + assert globalDispatcher != null; return globalDispatcher; } } diff --git a/utility-swing/src/main/java/com/dua3/utility/swing/ComboBoxEx.java b/utility-swing/src/main/java/com/dua3/utility/swing/ComboBoxEx.java index dfefc7d4..ec7d7818 100644 --- a/utility-swing/src/main/java/com/dua3/utility/swing/ComboBoxEx.java +++ b/utility-swing/src/main/java/com/dua3/utility/swing/ComboBoxEx.java @@ -1,5 +1,6 @@ package com.dua3.utility.swing; +import com.dua3.utility.lang.LangUtil; import org.jspecify.annotations.Nullable; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -23,6 +24,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.BiPredicate; import java.util.function.Function; @@ -44,9 +46,9 @@ public class ComboBoxEx extends JPanel { private final Function format; private final DefaultComboBoxModel model; private final JComboBox comboBox; - private final JButton buttonEdit; - private final JButton buttonAdd; - private final JButton buttonRemove; + private final @Nullable JButton buttonEdit; + private final @Nullable JButton buttonAdd; + private final @Nullable JButton buttonRemove; /** * Constructs a ComboBoxEx with optional editing, adding, and removing functionality. @@ -143,12 +145,23 @@ public Component getListCellRendererComponent(JList list, @Nullable Object va } private void updateButtonStates() { - buttonEdit.setEnabled(model.getSelectedItem() != null); - buttonAdd.setEnabled(true); - buttonRemove.setEnabled(model.getSize() > 1 && model.getSelectedItem() != null); + if (buttonEdit != null) { + buttonEdit.setEnabled(model.getSelectedItem() != null); + } + if (buttonAdd != null) { + buttonAdd.setEnabled(true); + } + if (buttonRemove != null) { + buttonRemove.setEnabled(model.getSize() > 1 && model.getSelectedItem() != null); + } } private void editItem() { + if (edit == null) { + LOG.warn("editing not supported"); + return; + } + int idx = comboBox.getSelectedIndex(); if (idx >= 0) { T item = model.getElementAt(idx); @@ -163,7 +176,7 @@ private void editItem() { } private void addItem() { - Optional.ofNullable(add.get()).ifPresent(item -> { + Optional.ofNullable(add).map(Supplier::get).ifPresent(item -> { model.addElement(item); model.setSelectedItem(item); sortItems(); diff --git a/utility-swing/src/main/java/com/dua3/utility/swing/SwingLogPane.java b/utility-swing/src/main/java/com/dua3/utility/swing/SwingLogPane.java index a93d65b7..506fba32 100644 --- a/utility-swing/src/main/java/com/dua3/utility/swing/SwingLogPane.java +++ b/utility-swing/src/main/java/com/dua3/utility/swing/SwingLogPane.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.Function; /** @@ -426,7 +427,7 @@ public String get(LogEntry entry) { THROWABLE { @Override public String get(LogEntry entry) { - return entry.throwable().toString(); + return Objects.toString(entry.throwable()); } }; diff --git a/utility-swing/src/main/java/com/dua3/utility/swing/SwingUtil.java b/utility-swing/src/main/java/com/dua3/utility/swing/SwingUtil.java index 165744cb..00446dfc 100644 --- a/utility-swing/src/main/java/com/dua3/utility/swing/SwingUtil.java +++ b/utility-swing/src/main/java/com/dua3/utility/swing/SwingUtil.java @@ -5,6 +5,7 @@ package com.dua3.utility.swing; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import com.dua3.utility.data.Color; import com.dua3.utility.data.Pair; @@ -358,7 +359,7 @@ public static Optional showOpenDialog(Component parent, Path current, int @SafeVarargs private static Optional showFileDialog(Component parent, Path current, int selectionMode, BiFunction showDialog, - Pair... types) { + Pair<@NonNull String, @NonNull String @NonNull[]>... types) { File file; try { file = current.toFile().getAbsoluteFile(); @@ -368,7 +369,7 @@ private static Optional showFileDialog(Component parent, Path current, int } JFileChooser jfc = new JFileChooser(); - for (Pair entry : types) { + for (var entry : types) { jfc.addChoosableFileFilter(new FileNameExtensionFilter(entry.first(), entry.second())); } diff --git a/utility/src/main/java/com/dua3/utility/concurrent/ThreadFactoryBuilder.java b/utility/src/main/java/com/dua3/utility/concurrent/ThreadFactoryBuilder.java index 2ed9729a..c7e8071f 100644 --- a/utility/src/main/java/com/dua3/utility/concurrent/ThreadFactoryBuilder.java +++ b/utility/src/main/java/com/dua3/utility/concurrent/ThreadFactoryBuilder.java @@ -9,7 +9,7 @@ * A builder class for creating customized ThreadFactory instances. */ public class ThreadFactoryBuilder { - private ThreadGroup group = null; + private @Nullable ThreadGroup group = null; private long stackSize = 0; private String prefix = ""; private boolean daemon = false; @@ -93,7 +93,7 @@ public ThreadFactory build() { } private static class CustomThreadFactory implements ThreadFactory { - private final ThreadGroup group; + private final @Nullable ThreadGroup group; private final long stackSize; private final String prefix; private final boolean daemon; diff --git a/utility/src/main/java/com/dua3/utility/data/DataUtil.java b/utility/src/main/java/com/dua3/utility/data/DataUtil.java index b1617b79..c3900706 100644 --- a/utility/src/main/java/com/dua3/utility/data/DataUtil.java +++ b/utility/src/main/java/com/dua3/utility/data/DataUtil.java @@ -63,7 +63,7 @@ private DataUtil() { * @param target type * @return the object converted to the target class */ - public static T convert(@Nullable Object value, Class targetClass) { + public static T convert(@Nullable Object value, Class targetClass) { return convert(value, targetClass, false); } @@ -314,7 +314,8 @@ public static U[] convertToArray(Collection data, Class t * @return array containing the converted elements */ @SuppressWarnings("unchecked") - public static U[] convertToArray(Collection data, Class targetClass, boolean useConstructor) { + public static U[] convertToArray(Collection data, Class targetClass, boolean useConstructor) { + //noinspection DataFlowIssue - false positive return data.stream() .map(obj -> convert(obj, targetClass, useConstructor)) .toArray(n -> (U[]) Array.newInstance(targetClass, n)); @@ -373,7 +374,8 @@ public static List convert(Collection data, Class targetClass) { * @param the element target type * @return list containing the converted elements */ - public static List convert(Collection data, Class targetClass, boolean useConstructor) { + public static List convert(Collection data, Class targetClass, boolean useConstructor) { + //noinspection DataFlowIssue - false positive return data.stream() .map(obj -> convert(obj, targetClass, useConstructor)) .collect(Collectors.toList()); diff --git a/utility/src/main/java/com/dua3/utility/data/FileTreeNode.java b/utility/src/main/java/com/dua3/utility/data/FileTreeNode.java index 07656f24..8364d04a 100644 --- a/utility/src/main/java/com/dua3/utility/data/FileTreeNode.java +++ b/utility/src/main/java/com/dua3/utility/data/FileTreeNode.java @@ -1,5 +1,6 @@ package com.dua3.utility.data; +import com.dua3.utility.lang.LangUtil; import org.jspecify.annotations.Nullable; import java.io.IOException; @@ -117,6 +118,7 @@ public Stream stream() { @Override public T parent() { + LangUtil.check(parent != null, "parent() called on root node"); return parent; } diff --git a/utility/src/main/java/com/dua3/utility/data/Pair.java b/utility/src/main/java/com/dua3/utility/data/Pair.java index 8df72ba1..c6ead795 100644 --- a/utility/src/main/java/com/dua3/utility/data/Pair.java +++ b/utility/src/main/java/com/dua3/utility/data/Pair.java @@ -60,6 +60,19 @@ public static void addToMap(Map m, Iterable(first, second); } + /** + * Create a Pair. + * + * @param first the first member + * @param second the second member + * @param type of first member + * @param type of second member + * @return a new Pair + */ + public static Pair ofNonNull(T1 first, T2 second) { + return new Pair<>(first, second); + } + /** * Create a Pair. * @@ -106,7 +119,7 @@ public static void addToMap(Map m, Iterable Pair map(Function f) { + public Pair map(Function f) { return of(f.apply(first()), f.apply(second())); } diff --git a/utility/src/main/java/com/dua3/utility/data/TreeNode.java b/utility/src/main/java/com/dua3/utility/data/TreeNode.java index de5b69d6..7af7c627 100644 --- a/utility/src/main/java/com/dua3/utility/data/TreeNode.java +++ b/utility/src/main/java/com/dua3/utility/data/TreeNode.java @@ -30,7 +30,8 @@ default boolean isRoot() { /** * Get this node's parent. * - * @return this node's parent node or {@code null} if this node is a root node + * @return this node's parent node + * @throws IllegalStateException if called on the root node */ N parent(); diff --git a/utility/src/main/java/com/dua3/utility/io/Codecs.java b/utility/src/main/java/com/dua3/utility/io/Codecs.java index 491c004c..16e91e54 100644 --- a/utility/src/main/java/com/dua3/utility/io/Codecs.java +++ b/utility/src/main/java/com/dua3/utility/io/Codecs.java @@ -3,6 +3,8 @@ import com.dua3.utility.data.Pair; import com.dua3.utility.data.RGBColor; import com.dua3.utility.lang.LangUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -26,6 +28,7 @@ * Registered codecs for a class can be obtained by using {@link #get(Class)}. */ public class Codecs { + private static final Logger LOG = LogManager.getLogger(Codecs.class); private final Map> codecs = new HashMap<>(); @@ -93,16 +96,25 @@ public String name() { } @Override - public void encode(DataOutputStream os, C collection) throws IOException { - os.writeInt(collection.size()); - for (T item : collection) { - codec.encode(os, item); + public void encode(DataOutputStream os, @Nullable C collection) throws IOException { + if (collection == null) { + os.writeInt(Integer.MIN_VALUE); + } else { + os.writeInt(collection.size()); + for (T item : collection) { + codec.encode(os, item); + } } } @Override - public C decode(DataInputStream is) throws IOException { + public @Nullable C decode(DataInputStream is) throws IOException { int n = is.readInt(); + + if (n == Integer.MIN_VALUE) { + return null; + } + LangUtil.check(n >= 0, "negative size for collection: %d", n); C collection = construct.apply(n); for (int i = 0; i < n; i++) { @@ -153,14 +165,20 @@ public String name() { } @Override - public void encode(DataOutputStream os, M map) throws IOException { - ENTRIES_CODEC.encode(os, map.entrySet()); + public void encode(DataOutputStream os, @Nullable M map) throws IOException { + ENTRIES_CODEC.encode(os, map == null ? null : map.entrySet()); } @Override - public M decode(DataInputStream is) throws IOException { + public @Nullable M decode(DataInputStream is) throws IOException { + Collection> decoded = ENTRIES_CODEC.decode(is); + + if (decoded == null) { + return null; + } + M map = construct.get(); - ENTRIES_CODEC.decode(is).forEach(entry -> map.put(entry.getKey(), entry.getValue())); + decoded.forEach(entry -> map.put(entry.getKey(), entry.getValue())); return map; } }; diff --git a/utility/src/main/java/com/dua3/utility/io/CsvFormatException.java b/utility/src/main/java/com/dua3/utility/io/CsvFormatException.java index dfce4fb0..cf9c9457 100644 --- a/utility/src/main/java/com/dua3/utility/io/CsvFormatException.java +++ b/utility/src/main/java/com/dua3/utility/io/CsvFormatException.java @@ -12,6 +12,8 @@ */ package com.dua3.utility.io; +import org.jspecify.annotations.Nullable; + import java.io.IOException; import java.io.Serial; import java.net.URI; @@ -25,7 +27,7 @@ public class CsvFormatException extends IOException { @Serial private static final long serialVersionUID = 1L; - private final URI source; + private final @Nullable URI source; private final int line; /** @@ -35,7 +37,7 @@ public class CsvFormatException extends IOException { * @param source a text describing the source (preferably the filename) * @param line the line number of the CSV file where the error occurred */ - public CsvFormatException(String message, URI source, int line) { + public CsvFormatException(String message, @Nullable URI source, int line) { super(message); this.source = source; this.line = line; diff --git a/utility/src/main/java/com/dua3/utility/io/CsvReader.java b/utility/src/main/java/com/dua3/utility/io/CsvReader.java index 0799f7a0..e5a7d595 100644 --- a/utility/src/main/java/com/dua3/utility/io/CsvReader.java +++ b/utility/src/main/java/com/dua3/utility/io/CsvReader.java @@ -47,11 +47,11 @@ public class CsvReader extends CsvIo { private final RowBuilder rowBuilder; private final Pattern patternField; private final BufferedReader reader; - private final URI source; + private final @Nullable URI source; private int rowNumber; private int rowsRead; private int lineNumber; - private List columnNames; + private @Nullable List columnNames; private boolean ignoreExcessFields; private boolean ignoreMissingFields; @@ -243,7 +243,7 @@ public int getRowsRead() { * * @return the source URI */ - private URI getSource() { + private @Nullable URI getSource() { return source; } diff --git a/utility/src/main/java/com/dua3/utility/io/Decoder.java b/utility/src/main/java/com/dua3/utility/io/Decoder.java index 09309c5d..bde3531c 100644 --- a/utility/src/main/java/com/dua3/utility/io/Decoder.java +++ b/utility/src/main/java/com/dua3/utility/io/Decoder.java @@ -34,6 +34,7 @@ public interface Decoder { Collection collection = collectionConstructor.apply(size); for (int i = 0; i < size; i++) { + //noinspection DataFlowIssue - false positive collection.add(codec.decode(is)); } diff --git a/utility/src/main/java/com/dua3/utility/io/FileType.java b/utility/src/main/java/com/dua3/utility/io/FileType.java index 7991ccf1..54ca7172 100644 --- a/utility/src/main/java/com/dua3/utility/io/FileType.java +++ b/utility/src/main/java/com/dua3/utility/io/FileType.java @@ -38,7 +38,7 @@ * * @param the type corresponding to the data contained in files of this {@link FileType} instance. */ -public abstract class FileType implements Comparable> { +public abstract class FileType implements Comparable> { /** * Set of defined file types. diff --git a/utility/src/main/java/com/dua3/utility/io/IoOptions.java b/utility/src/main/java/com/dua3/utility/io/IoOptions.java index 5b5792ce..5f37ddbe 100644 --- a/utility/src/main/java/com/dua3/utility/io/IoOptions.java +++ b/utility/src/main/java/com/dua3/utility/io/IoOptions.java @@ -53,7 +53,7 @@ public static ChoiceOption locale() { "--locale") .displayName("Locale") .description("set locale") - .defaultValue(Locale::getDefault); + .defaultSupplier(Locale::getDefault); } /** diff --git a/utility/src/main/java/com/dua3/utility/io/IoUtil.java b/utility/src/main/java/com/dua3/utility/io/IoUtil.java index 164e87f4..821dcd7e 100644 --- a/utility/src/main/java/com/dua3/utility/io/IoUtil.java +++ b/utility/src/main/java/com/dua3/utility/io/IoUtil.java @@ -6,7 +6,6 @@ package com.dua3.utility.io; import org.jspecify.annotations.Nullable; -import com.dua3.utility.data.Pair; import com.dua3.utility.lang.LangUtil; import com.dua3.utility.lang.Platform; import com.dua3.utility.text.TextUtil; @@ -91,7 +90,7 @@ private IoUtil() { // utility class } - private static record FileNameInfo(int idxStart, int idxEnd) {} + private record FileNameInfo(int idxStart, int idxEnd) {} /** * Extract the filename from a path given as a String. In addition to the system dependent @@ -462,6 +461,7 @@ public static Path toPath(String s) { */ public static void deleteRecursive(Path path) throws IOException { try (Stream files = Files.walk(path, FileVisitOption.FOLLOW_LINKS)) { + //noinspection DataFlowIssue - false positive files .sorted(Comparator.reverseOrder()) .forEach(LangUtil.uncheckedConsumer(Files::deleteIfExists)); diff --git a/utility/src/main/java/com/dua3/utility/io/SoftResource.java b/utility/src/main/java/com/dua3/utility/io/SoftResource.java index b71b1a90..366198f3 100644 --- a/utility/src/main/java/com/dua3/utility/io/SoftResource.java +++ b/utility/src/main/java/com/dua3/utility/io/SoftResource.java @@ -5,6 +5,8 @@ package com.dua3.utility.io; +import org.jspecify.annotations.Nullable; + import java.lang.ref.SoftReference; import java.util.function.Supplier; @@ -17,12 +19,16 @@ */ public final class SoftResource { - private Supplier supplier; + public static final SoftReference EMPTY_REFERENCE = new SoftReference<>(null); + public static final SoftResource EMPTY_RESOURCE = new SoftResource<>(null); + + private @Nullable Supplier supplier; private SoftReference ref; - private SoftResource(Supplier supplier) { + private SoftResource(@Nullable Supplier supplier) { this.supplier = supplier; - this.ref = new SoftReference<>(null); + //noinspection unchecked + this.ref = (SoftReference) EMPTY_REFERENCE; } /** @@ -33,7 +39,7 @@ private SoftResource(Supplier supplier) { * invocation return equal instances * @return soft resource */ - public static SoftResource of(Supplier supplier) { + public static SoftResource of(Supplier supplier) { return new SoftResource<>(supplier); } @@ -44,7 +50,8 @@ public static SoftResource of(Supplier supplier) { * @return empty soft resource */ public static SoftResource emptyReference() { - return new SoftResource<>(null); + //noinspection unchecked + return (SoftResource) EMPTY_RESOURCE; } /** @@ -54,7 +61,7 @@ public static SoftResource emptyReference() { * if it has not yet been set or has been garbage collected, it will be * restored by invoking the supplier */ - public T get() { + public @Nullable T get() { T obj = ref.get(); if (obj == null && supplier != null) { obj = supplier.get(); @@ -63,6 +70,7 @@ public T get() { supplier = null; } + //noinspection DataFlowIssue - false positive ref = new SoftReference<>(obj); } return obj; @@ -91,7 +99,7 @@ public ResourceHolder hold() { /** * Helper class to prevent the resource from being garbage collected. */ - public static final class ResourceHolder implements AutoCloseable { + public static final class ResourceHolder implements AutoCloseable { private final SoftResource soft; private T strong; diff --git a/utility/src/main/java/com/dua3/utility/lang/BatchCollector.java b/utility/src/main/java/com/dua3/utility/lang/BatchCollector.java index dea7f7f0..e68ddf95 100644 --- a/utility/src/main/java/com/dua3/utility/lang/BatchCollector.java +++ b/utility/src/main/java/com/dua3/utility/lang/BatchCollector.java @@ -22,7 +22,7 @@ * @param the item type * @param the key type */ -public class BatchCollector implements Collector>>, List>>> { +public class BatchCollector implements Collector>>, List>>> { private final Function keyMapper; private final K defaultKey; @@ -68,6 +68,7 @@ public BiConsumer>>, T> accumulator() { bucket = accu.peekLast().second(); } + //noinspection DataFlowIssue bucket.add(item); }; } diff --git a/utility/src/main/java/com/dua3/utility/lang/BuildInfo.java b/utility/src/main/java/com/dua3/utility/lang/BuildInfo.java index f7d00a3b..17f5fabe 100644 --- a/utility/src/main/java/com/dua3/utility/lang/BuildInfo.java +++ b/utility/src/main/java/com/dua3/utility/lang/BuildInfo.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.InputStream; import java.time.ZonedDateTime; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.regex.Matcher; @@ -80,7 +81,7 @@ public static BuildInfo create(Properties properties) { */ public static BuildInfo create(Class cls, String resource) throws IOException { try (InputStream in = cls.getResourceAsStream(resource)) { - return create(LangUtil.loadProperties(in)); + return create(LangUtil.loadProperties(Objects.requireNonNull(in, () -> "resource not found: " + resource))); } } diff --git a/utility/src/main/java/com/dua3/utility/lang/LangUtil.java b/utility/src/main/java/com/dua3/utility/lang/LangUtil.java index bdd9473e..6c175bd5 100644 --- a/utility/src/main/java/com/dua3/utility/lang/LangUtil.java +++ b/utility/src/main/java/com/dua3/utility/lang/LangUtil.java @@ -152,7 +152,7 @@ public static void check(boolean condition, String fmt, Object... args) { * @param the type * @return a, if a != null, else b */ - public static T orElse(@Nullable T a, T b) { + public static T orElse(T a, T b) { return a != null ? a : b; } @@ -164,7 +164,7 @@ public static void check(boolean condition, String fmt, Object... args) { * @param the type * @return a, if a != null, else b.get() */ - public static T orElseGet(@Nullable T a, Supplier b) { + public static T orElseGet(T a, Supplier b) { return a != null ? a : b.get(); } @@ -253,7 +253,7 @@ private static RuntimeException wrapException(Exception e) { * @throws UncheckedIOException if {@link IOException} is thrown during execution of the argument passed * @throws WrappedException if any other type of Exception is thrown during execution of the argument passed */ - public static Consumer uncheckedConsumer(ConsumerThrows c) { + public static Consumer uncheckedConsumer(ConsumerThrows c) { return arg -> { try { c.accept(arg); @@ -301,7 +301,7 @@ private static RuntimeException wrapException(Exception e) { * @throws WrappedException if any other type of Exception is thrown during execution of the argument passed */ @SuppressWarnings("ProhibitedExceptionThrown") - public static Function uncheckedFunction(FunctionThrows f) { + public static Function uncheckedFunction(FunctionThrows f) { return arg -> { try { return f.apply(arg); @@ -477,7 +477,7 @@ public static void putAll(Map map, Map.Entry. * @param supplier the Supplier * @return caching Supplier */ - public static Supplier cache(Supplier supplier) { + public static Supplier cache(Supplier supplier) { return new CachingSupplier<>(supplier, t -> {}); } @@ -1135,6 +1135,7 @@ private static class LazyFormatter { public LazyFormatter(@Nullable String fmt, Object... args) { if (fmt == null) { this.s = NULL_STRING; + //noinspection DataFlowIssue - false positive this.args = null; } else { this.s = fmt; @@ -1142,6 +1143,7 @@ public LazyFormatter(@Nullable String fmt, Object... args) { } } + @SuppressWarnings("DataFlowIssue") // false positive @Override public String toString() { if (args != null) { diff --git a/utility/src/main/java/com/dua3/utility/lang/RingBuffer.java b/utility/src/main/java/com/dua3/utility/lang/RingBuffer.java index f39f62f2..e6445c68 100644 --- a/utility/src/main/java/com/dua3/utility/lang/RingBuffer.java +++ b/utility/src/main/java/com/dua3/utility/lang/RingBuffer.java @@ -12,11 +12,11 @@ import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Set; /** * A ring buffer implementation. @@ -30,7 +30,7 @@ */ public class RingBuffer implements Collection { - private E[] data; + private @Nullable E[] data; private int entries; private int start; @@ -41,7 +41,7 @@ public class RingBuffer implements Collection { */ @SuppressWarnings("unchecked") public RingBuffer(int capacity) { - data = (E[]) new Object[capacity]; + data = (@Nullable E[]) new Object[capacity]; start = 0; entries = 0; } @@ -53,7 +53,7 @@ public RingBuffer(int capacity) { * @return true */ @Override - public boolean add(@Nullable E item) { + public boolean add(E item) { put(item); return true; } @@ -84,7 +84,7 @@ public boolean remove(@Nullable Object o) { @Override public boolean containsAll(Collection c) { - return Set.of(toArray()).containsAll(c); + return new HashSet<>(this).containsAll(c); } /** @@ -213,7 +213,7 @@ public Object[] toArray() { @Override @SuppressWarnings("unchecked") - public T[] toArray(T[] a) { + public @Nullable T[] toArray(@Nullable T[] a) { if (a.length < entries) { a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), entries); } @@ -306,6 +306,7 @@ public List subList(int fromIndex, int toIndex) { int sz = toIndex - fromIndex; Objects.checkFromIndexSize(fromIndex, sz, len); + //noinspection NullableProblems return new AbstractList<>() { @Override public E get(int index) { diff --git a/utility/src/main/java/com/dua3/utility/options/Arguments.java b/utility/src/main/java/com/dua3/utility/options/Arguments.java index e3aa22bb..0dfbea26 100644 --- a/utility/src/main/java/com/dua3/utility/options/Arguments.java +++ b/utility/src/main/java/com/dua3/utility/options/Arguments.java @@ -125,7 +125,7 @@ public void validate( options.forEach(entry -> hist.compute(entry.option, (k_, i_) -> i_ == null ? 1 : i_ + 1)); allOptions.stream() - .map(option -> Pair.of(option, hist.getOrDefault(option, 0))) + .map(option -> Pair.ofNonNull(option, hist.getOrDefault(option, 0))) .forEach(p -> { Option option = p.first(); int occurrences = p.second(); diff --git a/utility/src/main/java/com/dua3/utility/options/ArgumentsParserBuilder.java b/utility/src/main/java/com/dua3/utility/options/ArgumentsParserBuilder.java index 5402217f..0820ff76 100644 --- a/utility/src/main/java/com/dua3/utility/options/ArgumentsParserBuilder.java +++ b/utility/src/main/java/com/dua3/utility/options/ArgumentsParserBuilder.java @@ -2,6 +2,7 @@ import com.dua3.utility.data.DataUtil; import com.dua3.utility.lang.LangUtil; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.Map; @@ -108,7 +109,7 @@ public Flag flag(String... names) { * @param names the names for the option, at least one * @return the created SimpleOption */ - public SimpleOption simpleOption(Class type, String... names) { + public SimpleOption simpleOption(Class type, String... names) { return simpleOption(s -> DataUtil.convert(s, type, true), names); } @@ -120,7 +121,7 @@ public SimpleOption simpleOption(Class type, String... names * @param names the names for the option, at least one. * @return the created SimpleOption. */ - public SimpleOption simpleOption(Function mapper, String... names) { + public SimpleOption simpleOption(Function mapper, String... names) { return addOption(SimpleOption.create(mapper, names)); } @@ -144,7 +145,7 @@ public > ChoiceOption choiceOption(Class enumC * @param names the names for the option, at least one. * @return the created StandardOption. */ - public StandardOption option(Class type, String... names) { + public StandardOption option(Class type, String... names) { return option(s -> DataUtil.convert(s, type, true), names); } @@ -156,7 +157,7 @@ public StandardOption option(Class type, String... names) { * @param names the names for the option, at least one. * @return the created StandardOption. */ - public StandardOption option(Function mapper, String... names) { + public StandardOption option(Function mapper, String... names) { return addOption(StandardOption.create(mapper, names)); } diff --git a/utility/src/main/java/com/dua3/utility/options/ChoiceOption.java b/utility/src/main/java/com/dua3/utility/options/ChoiceOption.java index 7926bca8..e67d9497 100644 --- a/utility/src/main/java/com/dua3/utility/options/ChoiceOption.java +++ b/utility/src/main/java/com/dua3/utility/options/ChoiceOption.java @@ -19,7 +19,7 @@ public final class ChoiceOption extends Option { private final Supplier> values; - private Supplier defaultValue = () -> null; + private Supplier defaultSupplier = () -> null; /** * Constructor. @@ -143,17 +143,17 @@ public ChoiceOption handler(Consumer> handler) { * @return this option */ public ChoiceOption defaultValue(@Nullable T defaultValue) { - return defaultValue(() -> defaultValue); + return defaultSupplier(() -> defaultValue); } /** * Set default value. * - * @param defaultValue the default value + * @param defaultSupplier the default value * @return this option */ - public ChoiceOption defaultValue(Supplier defaultValue) { - this.defaultValue = defaultValue; + public ChoiceOption defaultSupplier(Supplier defaultSupplier) { + this.defaultSupplier = defaultSupplier; return this; } @@ -179,7 +179,7 @@ public ChoiceOption argName(String argName) { * @return Optional holding the default value. */ public Optional getDefault() { - return Optional.ofNullable(defaultValue.get()); + return Optional.ofNullable(defaultSupplier.get()); } /** diff --git a/utility/src/main/java/com/dua3/utility/options/SimpleOption.java b/utility/src/main/java/com/dua3/utility/options/SimpleOption.java index 525b7b6e..27f1d2fa 100644 --- a/utility/src/main/java/com/dua3/utility/options/SimpleOption.java +++ b/utility/src/main/java/com/dua3/utility/options/SimpleOption.java @@ -18,7 +18,7 @@ */ public final class SimpleOption extends Option { - private Supplier defaultValue = () -> null; + private Supplier defaultSupplier = () -> null; /** * Construct a new simple option with the given name(s). @@ -93,17 +93,17 @@ public SimpleOption handler(Consumer> handler) { * @return this option */ public SimpleOption defaultValue(@Nullable T defaultValue) { - return defaultValue(() -> defaultValue); + return defaultSupplier(() -> defaultValue); } /** * Set default value. * - * @param defaultValue the default value + * @param defaultSupplier the default value * @return this option */ - public SimpleOption defaultValue(Supplier defaultValue) { - this.defaultValue = defaultValue; + public SimpleOption defaultSupplier(Supplier defaultSupplier) { + this.defaultSupplier = defaultSupplier; return this; } @@ -124,7 +124,7 @@ public SimpleOption required() { */ @Override public Optional getDefault() { - return Optional.ofNullable(defaultValue.get()); + return Optional.ofNullable(defaultSupplier.get()); } } diff --git a/utility/src/main/java/com/dua3/utility/spi/SpiLoader.java b/utility/src/main/java/com/dua3/utility/spi/SpiLoader.java index 9510f4ad..25512fcb 100644 --- a/utility/src/main/java/com/dua3/utility/spi/SpiLoader.java +++ b/utility/src/main/java/com/dua3/utility/spi/SpiLoader.java @@ -82,7 +82,7 @@ public LoaderBuilder defaultSupplier(Supplier defaultSupplier) { */ public SpiLoader build() { Predicate p = predicate != null ? predicate : t -> true; - Supplier d = defaultSupplier != null ? defaultSupplier : () -> null; + Supplier d = defaultSupplier != null ? defaultSupplier : () -> null; ClassLoader c = cl != null ? cl : ClassLoader.getSystemClassLoader(); return new SpiLoader<>(type, c, p, d); diff --git a/utility/src/main/java/com/dua3/utility/text/AnsiConverter.java b/utility/src/main/java/com/dua3/utility/text/AnsiConverter.java index 2535f849..38d64c5a 100644 --- a/utility/src/main/java/com/dua3/utility/text/AnsiConverter.java +++ b/utility/src/main/java/com/dua3/utility/text/AnsiConverter.java @@ -2,6 +2,7 @@ import com.dua3.utility.data.Pair; import com.dua3.utility.io.AnsiCode; +import org.jspecify.annotations.Nullable; import java.util.ArrayDeque; import java.util.Collection; @@ -18,7 +19,7 @@ public final class AnsiConverter extends AttributeBasedConverter { private static final Map DEFAULT_ATTRIBUTES = new HashMap<>(); - private final HashMap> mappings = new HashMap<>(); + private final HashMap> mappings = new HashMap<>(); private boolean reset; private boolean reverseVideo; @@ -110,12 +111,12 @@ protected String get() { } @Override - protected void apply(Map> changedAttributes) { - Map attributes = new HashMap<>(); + protected void apply(Map> changedAttributes) { + Map attributes = new HashMap<>(); Deque tags = new ArrayDeque<>(); changedAttributes.forEach((attribute, values) -> { attributes.put(attribute, values.second()); - BiFunction mapping = mappings.get(attribute); + BiFunction<@Nullable Object, @Nullable Object, String> mapping = mappings.get(attribute); if (mapping != null) { tags.push(mapping.apply(values.first(), values.second())); } diff --git a/utility/src/main/java/com/dua3/utility/text/AttributeBasedConverter.java b/utility/src/main/java/com/dua3/utility/text/AttributeBasedConverter.java index 1812caf5..cfefe6e8 100644 --- a/utility/src/main/java/com/dua3/utility/text/AttributeBasedConverter.java +++ b/utility/src/main/java/com/dua3/utility/text/AttributeBasedConverter.java @@ -18,7 +18,7 @@ * * @param target type of conversion */ -public abstract class AttributeBasedConverter implements RichTextConverter { +public abstract class AttributeBasedConverter implements RichTextConverter { /** * Factory method to create a compatible converter implementation instance for this converter. @@ -124,7 +124,7 @@ private static void copyAttributes(Iterable newAttributes = collectAttributes(run); + Map newAttributes = collectAttributes(run); handleAttributeChanges(newAttributes); } diff --git a/utility/src/main/java/com/dua3/utility/text/AttributedCharacter.java b/utility/src/main/java/com/dua3/utility/text/AttributedCharacter.java index 3e81575f..4adfe03e 100644 --- a/utility/src/main/java/com/dua3/utility/text/AttributedCharacter.java +++ b/utility/src/main/java/com/dua3/utility/text/AttributedCharacter.java @@ -49,6 +49,8 @@ public TextAttributes attributes() { */ @SuppressWarnings("unchecked") default List