From b619ea66e23cf08908549ea5b053ca9c8a1de4de Mon Sep 17 00:00:00 2001 From: vananiev Date: Sun, 3 Nov 2024 22:04:17 +0300 Subject: [PATCH] fix all other "argument" checker framework warn --- checkerframework.astub | 76 +++++++++++++++++++ pom.xml | 4 +- .../entity/BeforeOrOnExecutionGenerator.java | 1 + .../openformat/v1_1_0/AccountPof.java | 2 + .../parser/InvestbookApiClient.java | 4 +- .../tinkoff/SecurityCodeAndIsinTable.java | 8 +- .../parser/uralsib/CashFlowTable.java | 4 +- .../parser/uralsib/PaymentsTable.java | 6 +- .../VtbCouponAmortizationRedemptionTable.java | 10 ++- .../parser/vtb/VtbSecuritiesTable.java | 4 +- .../VtbSecurityDepositAndWithdrawalTable.java | 6 +- .../parser/vtb/VtbSecurityFlowTable.java | 4 +- .../vtb/VtbSecurityTransactionTable.java | 3 +- .../report/DerivativeEventsFactory.java | 4 +- .../report/InternalRateOfReturn.java | 10 +-- .../report/PaidInterestFactory.java | 9 ++- .../java/ru/investbook/report/ViewFilter.java | 6 +- ...vesMarketTotalProfitExcelTableFactory.java | 3 +- .../report/excel/ExcelChartPlotHelper.java | 1 + .../investbook/report/excel/ExcelTable.java | 3 +- .../report/excel/ExcelTableView.java | 5 +- ...eignPortfolioPaymentExcelTableFactory.java | 11 +-- .../PortfolioAnalysisExcelTableFactory.java | 8 +- .../PortfolioAnalysisExcelTableView.java | 2 + .../PortfolioPaymentExcelTableFactory.java | 3 +- .../PortfolioStatusExcelTableFactory.java | 4 +- ...atusExcelTableFactoryProportionHelper.java | 2 +- .../report/excel/TaxExcelTableFactory.java | 3 +- .../repository/specs/SpecificationHelper.java | 3 +- .../service/InvestmentProportionService.java | 3 +- .../service/SecurityProfitServiceImpl.java | 13 ++-- .../ru/investbook/service/Sp500Service.java | 4 +- ...brForeignExchangeRateServiceExcelImpl.java | 6 +- .../moex/MoexDerivativeCodeService.java | 8 +- .../service/moex/MoexIssClient.java | 3 +- .../service/moex/MoexIssClientImpl.java | 3 +- .../service/moex/MoexSecurityQuoteHelper.java | 2 +- .../web/BrokerEmailReportController.java | 5 +- .../web/BrokerFileReportController.java | 3 +- .../SecurityEventCashFlowFormsService.java | 6 +- .../service/TransactionFormsService.java | 14 ++-- 41 files changed, 203 insertions(+), 76 deletions(-) create mode 100644 checkerframework.astub diff --git a/checkerframework.astub b/checkerframework.astub new file mode 100644 index 00000000..c0720267 --- /dev/null +++ b/checkerframework.astub @@ -0,0 +1,76 @@ +/* + * InvestBook + * Copyright (C) 2024 Spacious Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; + +package java.util; +public class Objects { + + @EnsuresNonNull("#1") + static T requireNonNull(@Nullable T obj); + + @EnsuresNonNull("#1") + static T requireNonNull(@Nullable T obj, String message); + + @EnsuresNonNull("#1") + static T requireNonNull(@Nullable T obj, Supplier messageSupplier); + + @EnsuresNonNullIf(expression="#1", result=true) + static boolean nonNull(@Nullable Object obj); + + @EnsuresNonNullIf(expression="#1", result=false) + static boolean isNull(@Nullable Object obj); +} + + +package org.springframework.util; +class StringUtils { + + @EnsuresNonNullIf(expression="#1", result=true) + static boolean hasText(@Nullable CharSequence str); + + @EnsuresNonNullIf(expression="#1", result=true) + static boolean hasText(@Nullable String str); + + @EnsuresNonNullIf(expression="#1", result=true) + static boolean hasLength(@Nullable CharSequence str); + + @EnsuresNonNullIf(expression="#1", result=true) + static boolean hasLength(@Nullable String str); +} + + +package org.springframework.util; +class CollectionUtils { + + @EnsuresNonNullIf(expression="#1", result=false) + static boolean isEmpty(@Nullable Collection collection); + + @EnsuresNonNullIf(expression="#1", result=false) + static boolean isEmpty(@Nullable Map map); +} + + +package org.slf4j; +interface Logger { + + void warn(String format, @Nullable Object arg1, @Nullable Object arg2); + + void warn(String format, @Nullable Object... arguments); +} diff --git a/pom.xml b/pom.xml index a20cc76b..f81c6878 100644 --- a/pom.xml +++ b/pom.xml @@ -304,10 +304,10 @@ - -Astubs=permit-nullness-assertion-exception.astub + -Astubs=permit-nullness-assertion-exception.astub${path.separator}checkerframework.astub - -AsuppressWarnings=dereference.of.nullable,argument,methodref,type.arguments.not.inferred,lambda.param,expression.unparsable + -AsuppressWarnings=dereference.of.nullable,methodref,type.arguments.not.inferred,lambda.param,expression.unparsable -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED diff --git a/src/main/java/ru/investbook/entity/BeforeOrOnExecutionGenerator.java b/src/main/java/ru/investbook/entity/BeforeOrOnExecutionGenerator.java index 416a45ce..852d47bb 100644 --- a/src/main/java/ru/investbook/entity/BeforeOrOnExecutionGenerator.java +++ b/src/main/java/ru/investbook/entity/BeforeOrOnExecutionGenerator.java @@ -74,6 +74,7 @@ public boolean generatedOnExecution() { public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { try { EventType eventType = beforeExecutionGenerator.getEventTypes().iterator().next(); + @SuppressWarnings("argument") Object id = beforeExecutionGenerator.generate(session, entity, null, eventType); return isNull(id); } catch (Exception e) { diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java index 7b7a9a7d..68fcc202 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java @@ -54,7 +54,9 @@ @Slf4j public class AccountPof { + @SuppressWarnings("type.argument") private static final ThreadLocal idGenerator = ThreadLocal.withInitial(AtomicInteger::new); + @SuppressWarnings("type.argument") private static final ThreadLocal> accountNumberToIdMap = ThreadLocal.withInitial(HashMap::new); @JsonProperty("id") diff --git a/src/main/java/ru/investbook/parser/InvestbookApiClient.java b/src/main/java/ru/investbook/parser/InvestbookApiClient.java index 2034caa5..498d2c2c 100644 --- a/src/main/java/ru/investbook/parser/InvestbookApiClient.java +++ b/src/main/java/ru/investbook/parser/InvestbookApiClient.java @@ -193,7 +193,7 @@ private boolean saveWithoutUpdate(T object, Consumer persistFunction, Str persistFunction.accept(object); return true; } catch (ConstraintViolationException e) { // jakarta.validation, not SQL constraint - log.warn("{} {}: {}", errorMsg, object, e.getMessage()); + log.warn("{}, {}: {}", errorMsg, e.getMessage(), object); return false; } catch (Exception e) { if (isUniqIndexViolationException(e)) { @@ -219,7 +219,7 @@ private Optional saveWithoutUpdateAndGet(T object, Function getRow(CashFlowEventTable.CashFlowEv .intValueExact(); case AMORTIZATION -> value.divide(getAmortizationPerOneBond(lowercaseDescription), 2, RoundingMode.HALF_UP) .intValueExact(); - case REDEMPTION -> vtbSecurityDepositAndWithdrawalTable.getBondRedemptionCount(security.getIsin()) + case REDEMPTION -> vtbSecurityDepositAndWithdrawalTable.getBondRedemptionCount(requireNonNull(security.getIsin())) .orElseThrow(() -> new IllegalArgumentException("Не удалось определить количество погашенных облигаций " + security.getIsin())); default -> throw new UnsupportedOperationException(); }; - int securityId = getReport().getSecurityRegistrar().declareBondByIsin(security.getIsin(), security::toBuilder); + int securityId = getReport().getSecurityRegistrar().declareBondByIsin(requireNonNull(security.getIsin()), security::toBuilder); Instant instant = event.getDate(); if (eventType != COUPON) { // gh-510: чтобы отличать налог на купон, событие налога и купона сдвигаем по времени от амортизации (погашения) @@ -132,7 +132,8 @@ private Security getSecurity(String description) { private static BigDecimal getCouponPerOneBond(String description) { Matcher matcher = couponPerOneBondPattern.matcher(description); if (matcher.find()) { - return BigDecimal.valueOf(parseDouble(matcher.group(1))); + String value = requireNonNull(matcher.group(1)); + return BigDecimal.valueOf(parseDouble(value)); } throw new IllegalArgumentException("Не смогу выделить размер купона на одну облигацию из описания: " + description); } @@ -140,7 +141,8 @@ private static BigDecimal getCouponPerOneBond(String description) { private static BigDecimal getAmortizationPerOneBond(String description) { Matcher matcher = amortizationPerOneBondPattern.matcher(description); if (matcher.find()) { - return BigDecimal.valueOf(parseDouble(matcher.group(1))); + String value = requireNonNull(matcher.group(1)); + return BigDecimal.valueOf(parseDouble(value)); } throw new IllegalArgumentException("Не смогу выделить размер купона на одну облигацию из описания: " + description); } diff --git a/src/main/java/ru/investbook/parser/vtb/VtbSecuritiesTable.java b/src/main/java/ru/investbook/parser/vtb/VtbSecuritiesTable.java index 9c1b4fa7..43424a52 100644 --- a/src/main/java/ru/investbook/parser/vtb/VtbSecuritiesTable.java +++ b/src/main/java/ru/investbook/parser/vtb/VtbSecuritiesTable.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.Map; +import static java.util.Objects.requireNonNull; import static ru.investbook.parser.vtb.VtbSecuritiesTable.VtbSecuritiesTableHeader.NAME_REGNUMBER_ISIN; import static ru.investbook.parser.vtb.VtbSecuritiesTable.VtbSecuritiesTableHeader.SECTION; @@ -53,7 +54,8 @@ protected VtbSecuritiesTable(SingleBrokerReport report) { } String description = row.getStringCellValue(NAME_REGNUMBER_ISIN); Security security = VtbReportHelper.getSecurity(description); - int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(security.getIsin(), security::toBuilder); + String isin = requireNonNull(security.getIsin()); + int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(isin, security::toBuilder); security = security.toBuilder().id(securityId).build(); String registrationNumber = description.split(",")[1].toUpperCase().trim(); regNumberToSecurity.put(registrationNumber, security); diff --git a/src/main/java/ru/investbook/parser/vtb/VtbSecurityDepositAndWithdrawalTable.java b/src/main/java/ru/investbook/parser/vtb/VtbSecurityDepositAndWithdrawalTable.java index e3d8e575..d0467238 100644 --- a/src/main/java/ru/investbook/parser/vtb/VtbSecurityDepositAndWithdrawalTable.java +++ b/src/main/java/ru/investbook/parser/vtb/VtbSecurityDepositAndWithdrawalTable.java @@ -32,6 +32,7 @@ import java.util.Optional; import java.util.Set; +import static java.util.Objects.requireNonNull; import static ru.investbook.parser.vtb.VtbSecurityFlowTable.VtbSecurityFlowTableHeader.*; public class VtbSecurityDepositAndWithdrawalTable extends SingleAbstractReportTable { @@ -69,8 +70,9 @@ protected VtbSecurityDepositAndWithdrawalTable(SingleBrokerReport report) { String description = row.getStringCellValue(NAME_REGNUMBER_ISIN); Security security = VtbReportHelper.getSecurity(description); Instant timestamp = row.getInstantCellValue(DATE); - String tradeId = generateTradeId(portfolio, timestamp, security.getIsin()); - int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(security.getIsin(), security::toBuilder); + String isin = requireNonNull(security.getIsin()); + String tradeId = generateTradeId(portfolio, timestamp, isin); + int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(isin, security::toBuilder); return SecurityTransaction.builder() .tradeId(tradeId) diff --git a/src/main/java/ru/investbook/parser/vtb/VtbSecurityFlowTable.java b/src/main/java/ru/investbook/parser/vtb/VtbSecurityFlowTable.java index cc5eff7b..a395fcf5 100644 --- a/src/main/java/ru/investbook/parser/vtb/VtbSecurityFlowTable.java +++ b/src/main/java/ru/investbook/parser/vtb/VtbSecurityFlowTable.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Map; +import static java.util.Objects.requireNonNull; import static ru.investbook.parser.vtb.VtbSecurityFlowTable.VtbSecurityFlowTableHeader.NAME_REGNUMBER_ISIN; public class VtbSecurityFlowTable extends SingleAbstractReportTable { @@ -46,7 +47,8 @@ protected VtbSecurityFlowTable(SingleBrokerReport report) { protected Security parseRow(TableRow row) { String description = row.getStringCellValue(NAME_REGNUMBER_ISIN); Security security = VtbReportHelper.getSecurity(description); - int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(security.getIsin(), security::toBuilder); + String isin = requireNonNull(security.getIsin()); + int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(isin, security::toBuilder); security = security.toBuilder().id(securityId).build(); String registrationNumber = description.split(",")[1].toUpperCase().trim(); regNumberToSecurity.put(registrationNumber, security); diff --git a/src/main/java/ru/investbook/parser/vtb/VtbSecurityTransactionTable.java b/src/main/java/ru/investbook/parser/vtb/VtbSecurityTransactionTable.java index fea77e9a..dbe9a99a 100644 --- a/src/main/java/ru/investbook/parser/vtb/VtbSecurityTransactionTable.java +++ b/src/main/java/ru/investbook/parser/vtb/VtbSecurityTransactionTable.java @@ -33,6 +33,7 @@ import java.util.HashSet; import java.util.Set; +import static java.util.Objects.requireNonNull; import static ru.investbook.parser.vtb.VtbBrokerReport.minValue; import static ru.investbook.parser.vtb.VtbSecurityTransactionTable.VtbSecurityTransactionTableHeader.*; @@ -68,7 +69,7 @@ protected SecurityTransaction parseRow(TableRow row) { Security security = VtbReportHelper.getSecurity(row.getStringCellValue(NAME_AND_ISIN)); securities.add(security); - int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(security.getIsin(), security::toBuilder); + int securityId = getReport().getSecurityRegistrar().declareStockOrBondByIsin(requireNonNull(security.getIsin()), security::toBuilder); return SecurityTransaction.builder() .timestamp(row.getInstantCellValue(DATE)) diff --git a/src/main/java/ru/investbook/report/DerivativeEventsFactory.java b/src/main/java/ru/investbook/report/DerivativeEventsFactory.java index 01603c36..83e5a9ae 100644 --- a/src/main/java/ru/investbook/report/DerivativeEventsFactory.java +++ b/src/main/java/ru/investbook/report/DerivativeEventsFactory.java @@ -52,6 +52,7 @@ import java.util.stream.Collectors; import static java.util.Collections.singleton; +import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; @Component @@ -78,7 +79,8 @@ public DerivativeEvents getDerivativeEvents(Portfolio portfolio, Security contra BigDecimal totalProfit = BigDecimal.ZERO; int currentPosition = 0; @Nullable LocalDate currentDay = firstEventDate; - while (currentDay != null && currentDay.compareTo(lastEventDate) <= 0) { + //noinspection ConstantValue + while (nonNull(currentDay) && nonNull(lastEventDate) && currentDay.compareTo(lastEventDate) <= 0) { Deque dailyTransactions = getDailyTransactions(transactions, currentDay); @Nullable SecurityEventCashFlow cash = securityEventCashFlows.get(currentDay); //noinspection ConstantValue diff --git a/src/main/java/ru/investbook/report/InternalRateOfReturn.java b/src/main/java/ru/investbook/report/InternalRateOfReturn.java index 53137cf9..804471d0 100644 --- a/src/main/java/ru/investbook/report/InternalRateOfReturn.java +++ b/src/main/java/ru/investbook/report/InternalRateOfReturn.java @@ -72,11 +72,11 @@ public class InternalRateOfReturn { * Возвращает внутреннюю норму доходности вложений. Не рассчитывается для срочных инструментов, т.к. * вложение (гарантийное обеспечение) не хранится на данный момент в БД. * - * @param quote may be null only if current security position is zero - * @return internal rate of return if can be calculated or null otherwise + * @param quote may be null, if current security position equals to 0. Otherwise, null result is returned + * @return internal rate of return, if it can be calculated, or null otherwise */ public @Nullable Double calc( - Collection portfolios, Security security, SecurityQuote quote, Instant fromDate, Instant toDate) { + Collection portfolios, Security security, @Nullable SecurityQuote quote, Instant fromDate, Instant toDate) { try { boolean isDerivative = (security.getType() == DERIVATIVE); @@ -138,12 +138,12 @@ private org.decampo.xirr.Transaction castToXirrTransaction(SecurityEventCashFlow toLocalDate(cashFlowEntity.getTimestamp())); } - private Optional castToXirrTransaction(SecurityQuote quote, + private Optional castToXirrTransaction(@Nullable SecurityQuote quote, String toCurrency, int positionCount, SecurityType securityType) { return ofNullable(quote) .map(_quote -> _quote.getDirtyPriceInCurrency(securityType == DERIVATIVE)) - .map(dirtyPrice -> convertToCurrency(dirtyPrice, quote.getCurrency(), toCurrency)) + .map(dirtyPrice -> convertToCurrency(dirtyPrice, requireNonNull(quote.getCurrency()), toCurrency)) .map(dirtyPrice -> new org.decampo.xirr.Transaction( positionCount * dirtyPrice.doubleValue(), toLocalDate(quote.getTimestamp()))); diff --git a/src/main/java/ru/investbook/report/PaidInterestFactory.java b/src/main/java/ru/investbook/report/PaidInterestFactory.java index e9cc221c..2b9ab5bf 100644 --- a/src/main/java/ru/investbook/report/PaidInterestFactory.java +++ b/src/main/java/ru/investbook/report/PaidInterestFactory.java @@ -45,6 +45,7 @@ import java.util.stream.Collectors; import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; import static org.spacious_team.broker.pojo.CashFlowType.*; @Component @@ -69,7 +70,7 @@ private PaidInterest create( for (CashFlowType type : PAY_TYPES) { paidInterest.get(type) .putAll(getPositionWithPayments( - portfolio, security.getId(), positions, type, fromDate, toDate)); + portfolio, requireNonNull(security.getId()), positions, type, fromDate, toDate)); } return paidInterest; } @@ -103,12 +104,12 @@ private Map> getPositionWithPayments(Strin getPayments(cash, paidPositions).forEach((position, cashs) -> cashs.forEach(securityCash -> - payments.computeIfAbsent(position, p -> new ArrayList<>()) + payments.computeIfAbsent(position, _ -> new ArrayList<>()) .add(securityCash))); } catch (Exception e) { log.warn("{}, выплата будет отображена в отчете по фиктивной позиции покупки ЦБ от даты {}", e.getMessage(), PaidInterest.fictitiousPositionInstant); - payments.computeIfAbsent(PaidInterest.getFictitiousPositionPayment(cash), key -> new ArrayList<>()) + payments.computeIfAbsent(PaidInterest.getFictitiousPositionPayment(cash), _ -> new ArrayList<>()) .add(cash); } } @@ -165,7 +166,7 @@ private Map> getPayments(SecurityEventCash BigDecimal pay = payPerOne .multiply(BigDecimal.valueOf(count)) .setScale(6, RoundingMode.HALF_UP); - payments.computeIfAbsent(position, key -> new ArrayList<>()) + payments.computeIfAbsent(position, _ -> new ArrayList<>()) .add(cash.toBuilder() .count(count) .value(pay) diff --git a/src/main/java/ru/investbook/report/ViewFilter.java b/src/main/java/ru/investbook/report/ViewFilter.java index ed812785..6180fc18 100644 --- a/src/main/java/ru/investbook/report/ViewFilter.java +++ b/src/main/java/ru/investbook/report/ViewFilter.java @@ -21,6 +21,7 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.web.model.ViewFilterModel; import java.time.Instant; @@ -32,12 +33,13 @@ import java.util.function.Supplier; import static java.time.ZoneId.systemDefault; +import static java.util.Objects.requireNonNull; @Getter @Builder(toBuilder = true) @EqualsAndHashCode public class ViewFilter { - private static final ThreadLocal filters = ThreadLocal.withInitial(() -> null); + private static final ThreadLocal<@Nullable ViewFilter> filters = ThreadLocal.withInitial(() -> null); public static final Instant defaultFromDate = Instant.ofEpochSecond(0); private static final Function toInstant = date -> date.atStartOfDay(systemDefault()).toInstant(); @@ -78,7 +80,7 @@ public static void set(ViewFilter viewFilter) { } public static ViewFilter get() { - return filters.get(); + return requireNonNull(filters.get(), "ViewFilter is not set"); } public static void remove() { diff --git a/src/main/java/ru/investbook/report/excel/DerivativesMarketTotalProfitExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/DerivativesMarketTotalProfitExcelTableFactory.java index df1f9394..2e551ed9 100644 --- a/src/main/java/ru/investbook/report/excel/DerivativesMarketTotalProfitExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/DerivativesMarketTotalProfitExcelTableFactory.java @@ -53,6 +53,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.*; import static org.spacious_team.broker.pojo.CashFlowType.DERIVATIVE_PROFIT; @@ -184,7 +185,7 @@ private Deque getTransactions(Collection portfolios, Set positionsFactory.getTransactions(contract.getId(), pf)) + .map(contract -> positionsFactory.getTransactions(requireNonNull(contract.getId()), pf)) .flatMap(Collection::stream) .sorted(Comparator.comparing(Transaction::getTimestamp)) .collect(toCollection(LinkedList::new)); diff --git a/src/main/java/ru/investbook/report/excel/ExcelChartPlotHelper.java b/src/main/java/ru/investbook/report/excel/ExcelChartPlotHelper.java index 6f8ae8e3..ea5b12fe 100644 --- a/src/main/java/ru/investbook/report/excel/ExcelChartPlotHelper.java +++ b/src/main/java/ru/investbook/report/excel/ExcelChartPlotHelper.java @@ -82,6 +82,7 @@ static XDDFChartData createScatterChartData(XSSFChart chart) { static XDDFChartData createPieChartData(XSSFChart chart) { setPieChartStyle(chart); + @SuppressWarnings("argument") XDDFChartData data = chart.createData(ChartTypes.PIE, null, null); data.setVaryColors(true); return data; diff --git a/src/main/java/ru/investbook/report/excel/ExcelTable.java b/src/main/java/ru/investbook/report/excel/ExcelTable.java index 3d80b320..576be00a 100644 --- a/src/main/java/ru/investbook/report/excel/ExcelTable.java +++ b/src/main/java/ru/investbook/report/excel/ExcelTable.java @@ -23,6 +23,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Portfolio; import ru.investbook.report.Table; @@ -30,7 +31,7 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @Slf4j public class ExcelTable { - private final Portfolio portfolio; + private final @Nullable Portfolio portfolio; private final String sheetName; private final Table table; private final ExcelTableView creator; diff --git a/src/main/java/ru/investbook/report/excel/ExcelTableView.java b/src/main/java/ru/investbook/report/excel/ExcelTableView.java index 79445a24..3100ccb6 100644 --- a/src/main/java/ru/investbook/report/excel/ExcelTableView.java +++ b/src/main/java/ru/investbook/report/excel/ExcelTableView.java @@ -48,7 +48,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Map; import java.util.Optional; import java.util.function.UnaryOperator; import java.util.regex.Pattern; @@ -106,7 +105,7 @@ protected Collection getPortfolios(Collection allowedPo * * @param portfolio accept null or portfolio */ - public & TableHeader> void createSheet(Portfolio portfolio, + public & TableHeader> void createSheet(@Nullable Portfolio portfolio, Workbook book, String sheetName, Table table, @@ -124,7 +123,7 @@ public & TableHeader> void createSheet(Portfolio portfolio, table.addFirst(totalRow); } int rowNum = 0; - for (Map tableRow : table) { + for (Table.Record tableRow : table) { Row row = sheet.createRow(++rowNum); TableHeader[] tableHeader = requireNonNull(headerType.getEnumConstants()); for (TableHeader header : tableHeader) { diff --git a/src/main/java/ru/investbook/report/excel/ForeignPortfolioPaymentExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/ForeignPortfolioPaymentExcelTableFactory.java index 38750706..3ae3cc8c 100644 --- a/src/main/java/ru/investbook/report/excel/ForeignPortfolioPaymentExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/ForeignPortfolioPaymentExcelTableFactory.java @@ -20,7 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.broker.pojo.Portfolio; @@ -36,7 +36,6 @@ import java.time.LocalDate; import java.time.Month; import java.time.ZoneId; -import java.time.format.TextStyle; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -44,6 +43,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.time.format.TextStyle.FULL_STANDALONE; +import static java.util.Objects.requireNonNull; import static org.spacious_team.broker.pojo.CashFlowType.*; import static ru.investbook.report.excel.ForeignPortfolioPaymentExcelTableHeader.*; import static ru.investbook.report.excel.TaxExcelTableFactory.isDividendOrCouponTax; @@ -87,7 +88,7 @@ private Table getTable(List cashFlows) { table.add(new Table.Record()); Table.Record monthTotalRecord = new Table.Record(); table.add(monthTotalRecord); - @Nullable Month month = null; + @MonotonicNonNull Month month = null; int sumRowCount = 0; for (EventCashFlow cash : cashFlows) { Instant timestamp = cash.getTimestamp(); @@ -112,7 +113,7 @@ private Table getTable(List cashFlows) { table.add(record); sumRowCount++; } - calcTotalRecord(monthTotalRecord, month, sumRowCount); + calcTotalRecord(monthTotalRecord, requireNonNull(month), sumRowCount); if (!cashFlows.isEmpty()) { foreignExchangeRateTableFactory.appendExchangeRates(table, CURRENCY_NAME, EXCHANGE_RATE); } @@ -122,7 +123,7 @@ private Table getTable(List cashFlows) { private void calcTotalRecord(Table.Record monthTotalRecord, Month month, int sumRowCount) { if (sumRowCount != 0) { - monthTotalRecord.put(DATE, StringUtils.capitalize(month.getDisplayName(TextStyle.FULL_STANDALONE, Locale.getDefault()))); + monthTotalRecord.put(DATE, StringUtils.capitalize(month.getDisplayName(FULL_STANDALONE, Locale.getDefault()))); monthTotalRecord.put(CASH_RUB, "=SUM(OFFSET(" + CASH_RUB.getCellAddr() + ",1,0," + sumRowCount + ",1))"); } } diff --git a/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableFactory.java index 2e01d420..7ab13ea4 100644 --- a/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableFactory.java @@ -63,6 +63,7 @@ import static java.lang.Double.parseDouble; import static java.util.Collections.singleton; import static java.util.Comparator.comparing; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.*; import static org.spacious_team.broker.pojo.CashFlowType.CASH; import static org.spacious_team.broker.pojo.PortfolioPropertyType.TOTAL_ASSETS_RUB; @@ -133,10 +134,10 @@ private Table createTable(List cashFlows, private void addInvestmentColumns(List cashFlows, Table table) { for (EventCashFlow cashFlow : cashFlows) { Table.Record record = recordOf(table, cashFlow.getTimestamp(), cashFlow.getCurrency()); - record.merge(INVESTMENT_AMOUNT, cashFlow.getValue(), (v1, v2) -> ((BigDecimal) v1).add(((BigDecimal) v2))); - record.computeIfAbsent(INVESTMENT_AMOUNT_USD, $ -> foreignExchangeRateTableFactory + record.merge(INVESTMENT_AMOUNT, cashFlow.getValue(), (v1, v2) -> ((BigDecimal) v1).add(requireNonNull(((BigDecimal) v2)))); + record.computeIfAbsent(INVESTMENT_AMOUNT_USD, _ -> foreignExchangeRateTableFactory .cashConvertToUsdExcelFormula(cashFlow.getCurrency(), INVESTMENT_AMOUNT, EXCHANGE_RATE)); - record.computeIfAbsent(TOTAL_INVESTMENT_USD, $ -> + record.computeIfAbsent(TOTAL_INVESTMENT_USD, _ -> "=SUM(" + INVESTMENT_AMOUNT_USD.getColumnIndex() + "3:" + INVESTMENT_AMOUNT_USD.getCellAddr() + ")"); } } @@ -234,6 +235,7 @@ private static void addSp500GrowthColumn(Map sp500, Table boolean isSp500ValueKnown = false; for (Table.Record record : table) { @Nullable LocalDate date = (LocalDate) record.get(DATE); + @SuppressWarnings("argument") @Nullable BigDecimal value = sp500.get(date); if (value != null) { isSp500ValueKnown = true; diff --git a/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableView.java b/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableView.java index f99ed1e0..f0b9c383 100644 --- a/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableView.java +++ b/src/main/java/ru/investbook/report/excel/PortfolioAnalysisExcelTableView.java @@ -143,6 +143,7 @@ protected void sheetPostCreate(Sheet sheet, Class headerT plotChart("Остаток денежных средств, USD", sheet, PortfolioAnalysisExcelTableView::addCashBalanceGraph); } + @SuppressWarnings("argument") private static void addInvestmentAndAssetsGraph(String name, Sheet sheet) { int rowCount = sheet.getLastRowNum(); XSSFSheet _sheet = (XSSFSheet) sheet; @@ -166,6 +167,7 @@ private static void addInvestmentAndAssetsGraph(String name, Sheet sheet) { chart.plot(chartData); } + @SuppressWarnings("argument") private static void addPortfolioGrowthGraph(String name, Sheet sheet) { int rowCount = sheet.getLastRowNum(); XSSFSheet _sheet = (XSSFSheet) sheet; diff --git a/src/main/java/ru/investbook/report/excel/PortfolioPaymentExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/PortfolioPaymentExcelTableFactory.java index f4cb315f..76811869 100644 --- a/src/main/java/ru/investbook/report/excel/PortfolioPaymentExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/PortfolioPaymentExcelTableFactory.java @@ -44,6 +44,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNull; import static org.spacious_team.broker.pojo.CashFlowType.*; import static ru.investbook.report.excel.PortfolioPaymentExcelTableHeader.*; @@ -113,7 +114,7 @@ private Table getTable(List cashFlows) { table.add(record); sumRowCount++; } - calcTotalRecord(monthTotalRecord, month, sumRowCount); + calcTotalRecord(monthTotalRecord, requireNonNull(month), sumRowCount); if (!cashFlows.isEmpty()) { foreignExchangeRateTableFactory.appendExchangeRates(table, CURRENCY_NAME, EXCHANGE_RATE); } diff --git a/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactory.java index ab5af466..1b546798 100644 --- a/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactory.java @@ -58,6 +58,7 @@ import java.util.stream.Collectors; import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.spacious_team.broker.pojo.SecurityType.*; import static ru.investbook.report.excel.PortfolioStatusExcelTableFactoryProportionHelper.setCurrentProportionFormula; @@ -197,7 +198,7 @@ private Table.Record getSecurityStatus(Collection portfolios, Security s SecurityType securityType = security.getType(); row.put(SECURITY, securityType == CURRENCY_PAIR ? - getCurrencyPair(security.getTicker()) : + getCurrencyPair(requireNonNull(security.getTicker())) : ofNullable(security.getName()) .or(() -> ofNullable(security.getTicker())) .orElse(security.getIsin())); @@ -270,7 +271,6 @@ private Table.Record getSecurityStatus(Collection portfolios, Security s row.put(TAX, securityProfitService.sumPaymentsForType(portfolios, security, CashFlowType.TAX, toCurrency).abs()); } row.put(PROFIT, PROFIT_FORMULA); - //noinspection DataFlowIssue row.put(INTERNAL_RATE_OF_RETURN, internalRateOfReturn.calc( portfolios, security, quote, filter.getFromDate(), filter.getToDate())); row.put(PROFIT_PROPORTION, PROFIT_PROPORTION_FORMULA); diff --git a/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactoryProportionHelper.java b/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactoryProportionHelper.java index ba203619..a40e9bc7 100644 --- a/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactoryProportionHelper.java +++ b/src/main/java/ru/investbook/report/excel/PortfolioStatusExcelTableFactoryProportionHelper.java @@ -94,7 +94,7 @@ private static BigDecimal getCurrentAmount(Table.Record record) { .multiply(toBigDecimal(record.get(COUNT))); } - private static BigDecimal toBigDecimal(Object value) { + private static BigDecimal toBigDecimal(@Nullable Object value) { if (value instanceof BigDecimal n) { return n; } diff --git a/src/main/java/ru/investbook/report/excel/TaxExcelTableFactory.java b/src/main/java/ru/investbook/report/excel/TaxExcelTableFactory.java index c2e079be..40796830 100644 --- a/src/main/java/ru/investbook/report/excel/TaxExcelTableFactory.java +++ b/src/main/java/ru/investbook/report/excel/TaxExcelTableFactory.java @@ -19,6 +19,7 @@ package ru.investbook.report.excel; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.broker.pojo.Portfolio; @@ -82,7 +83,7 @@ private void addRecordToTable(Table table, EventCashFlow cash) { table.add(record); } - static boolean isDividendOrCouponTax(String description) { + static boolean isDividendOrCouponTax(@Nullable String description) { if (description == null) { return false; } diff --git a/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java b/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java index 048b463d..9aff0443 100644 --- a/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java +++ b/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java @@ -27,7 +27,6 @@ import jakarta.persistence.metamodel.SingularAttribute; import lombok.NoArgsConstructor; import org.checkerframework.checker.nullness.qual.Nullable; -import org.springframework.lang.NonNull; import ru.investbook.entity.PortfolioEntity; import ru.investbook.entity.PortfolioEntity_; import ru.investbook.entity.SecurityEntity; @@ -189,7 +188,7 @@ static Predicate filterByPortfolioName(Root root, return null; } - private static Predicate filterByLike(CriteriaBuilder builder, Path path, @NonNull String value) { + private static Predicate filterByLike(CriteriaBuilder builder, Path path, String value) { return builder.like(builder.lower(path), "%" + value.toLowerCase() + "%"); } } diff --git a/src/main/java/ru/investbook/service/InvestmentProportionService.java b/src/main/java/ru/investbook/service/InvestmentProportionService.java index f1216cc3..cf2c8b2c 100644 --- a/src/main/java/ru/investbook/service/InvestmentProportionService.java +++ b/src/main/java/ru/investbook/service/InvestmentProportionService.java @@ -38,6 +38,7 @@ import java.util.stream.Stream; import static java.lang.System.nanoTime; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.*; import static org.spacious_team.broker.pojo.SecurityType.*; @@ -118,7 +119,7 @@ public Optional getOpenedPositionsCostByCurrentOrLastTransactionQuot } private String getEconomicSector(SecurityInvestment securityInvestment) { - return securityDescriptionRepository.findById(securityInvestment.security().getId()) + return securityDescriptionRepository.findById(requireNonNull(securityInvestment.security().getId())) .map(SecurityDescriptionEntity::getSector) .orElse(SecuritySectorService.UNKNOWN_SECTOR); } diff --git a/src/main/java/ru/investbook/service/SecurityProfitServiceImpl.java b/src/main/java/ru/investbook/service/SecurityProfitServiceImpl.java index 4ea05bf0..78469141 100644 --- a/src/main/java/ru/investbook/service/SecurityProfitServiceImpl.java +++ b/src/main/java/ru/investbook/service/SecurityProfitServiceImpl.java @@ -75,13 +75,14 @@ public class SecurityProfitServiceImpl implements SecurityProfitService { public Optional getLastEventTimestamp( Collection portfolios, Security security, Set events, Instant from, Instant to) { + Integer securityId = requireNonNull(security.getId()); Optional optional = portfolios.isEmpty() ? securityEventCashFlowRepository .findFirstBySecurityIdAndCashFlowTypeIdInAndTimestampBetweenOrderByTimestampDesc( - security.getId(), events, from, to) : + securityId, events, from, to) : securityEventCashFlowRepository .findFirstByPortfolioIdInAndSecurityIdAndCashFlowTypeIdInAndTimestampBetweenOrderByTimestampDesc( - portfolios, security.getId(), events, from, to); + portfolios, securityId, events, from, to); return optional.map(SecurityEventCashFlowEntity::getTimestamp); } @@ -222,17 +223,18 @@ public BigDecimal sumPaymentsForType(Collection portfolios, Security sec private List getSecurityEventCashFlowEntities(Collection portfolios, Security security, CashFlowType cashFlowType) { + Integer securityId = requireNonNull(security.getId()); return portfolios.isEmpty() ? securityEventCashFlowRepository .findBySecurityIdAndCashFlowTypeIdAndTimestampBetweenOrderByTimestampAsc( - security.getId(), + securityId, cashFlowType.getId(), ViewFilter.get().getFromDate(), ViewFilter.get().getToDate()) : securityEventCashFlowRepository .findByPortfolioIdInAndSecurityIdAndCashFlowTypeIdAndTimestampBetweenOrderByTimestampAsc( portfolios, - security.getId(), + securityId, cashFlowType.getId(), ViewFilter.get().getFromDate(), ViewFilter.get().getToDate()); @@ -267,7 +269,8 @@ private List getSecurityEventCashFlowEntities(Colle @Override public Optional getSecurityQuoteFromLastTransaction(Security security, String toCurrency) { - return transactionRepository.findFirstBySecurityIdOrderByTimestampDesc(security.getId()) + //noinspection ReturnOfNull + return transactionRepository.findFirstBySecurityIdOrderByTimestampDesc(requireNonNull(security.getId())) .map(t -> transactionCashFlowRepository .findByTransactionIdAndCashFlowTypeIn(t.getId(), priceAndAccruedInterestTypes) .stream() diff --git a/src/main/java/ru/investbook/service/Sp500Service.java b/src/main/java/ru/investbook/service/Sp500Service.java index 934fd9ee..825e91bf 100644 --- a/src/main/java/ru/investbook/service/Sp500Service.java +++ b/src/main/java/ru/investbook/service/Sp500Service.java @@ -41,6 +41,7 @@ import java.time.Duration; import java.util.Objects; +import static java.util.Objects.requireNonNull; import static org.springframework.web.util.UriComponentsBuilder.fromHttpUrl; @Service @@ -71,11 +72,12 @@ public void update() { } private void updateBy(@Nullable Resource resource) throws IOException { - Objects.requireNonNull(resource, () -> "Не удалось скачать S&P 500 с адреса " + uri); + requireNonNull(resource, () -> "Не удалось скачать S&P 500 с адреса " + uri); Workbook book = new HSSFWorkbook(resource.getInputStream()); new ExcelSheet(book.getSheetAt(0)) .createNameless("Effective date", TableHeader.class) .stream() + .filter(Objects::nonNull) .map(Sp500Service::getIndexValue) .forEach(this::save); } diff --git a/src/main/java/ru/investbook/service/cbr/CbrForeignExchangeRateServiceExcelImpl.java b/src/main/java/ru/investbook/service/cbr/CbrForeignExchangeRateServiceExcelImpl.java index 4b3d7ff5..49835b2d 100644 --- a/src/main/java/ru/investbook/service/cbr/CbrForeignExchangeRateServiceExcelImpl.java +++ b/src/main/java/ru/investbook/service/cbr/CbrForeignExchangeRateServiceExcelImpl.java @@ -41,6 +41,8 @@ import java.util.Map; import java.util.Objects; +import static java.util.Objects.requireNonNull; + public class CbrForeignExchangeRateServiceExcelImpl extends AbstractCbrForeignExchangeRateService { private static final String uri = "https://www.cbr.ru/Queries/UniDbQuery/DownloadExcel/98956?" + "VAL_NM_RQ={currency}&" + @@ -71,13 +73,13 @@ protected void updateCurrencyRate(String currencyPair, String currencyId, LocalD } private void updateBy(@Nullable Resource resource, String currencyPair) throws IOException { - Objects.requireNonNull(resource, () -> "Не удалось скачать курсы валют"); + requireNonNull(resource, "Не удалось скачать курсы валют"); Workbook book = new XSSFWorkbook(resource.getInputStream()); new ExcelSheet(book.getSheetAt(0)) .createNameless("data", TableHeader.class) .stream() .filter(Objects::nonNull) - .map(row -> getRate(row, currencyPair)) + .map(row -> getRate(requireNonNull(row), currencyPair)) .forEach(this::save); } diff --git a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java index 295d9e5d..ccbf9cc4 100644 --- a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java +++ b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java @@ -34,6 +34,7 @@ import static java.lang.Character.isAlphabetic; import static java.lang.Character.isDigit; import static java.lang.Integer.parseInt; +import static java.util.Objects.isNull; import static java.util.Optional.empty; import static java.util.stream.Collectors.toMap; @@ -197,6 +198,7 @@ public class MoexDerivativeCodeService { // 2000-th, 2010-th, 2020-th and so on private final int currentYearDecade = currentYear / 10 * 10; + @SuppressWarnings("argument") private MoexDerivativeCodeService() { this.shortnameToCodes = codeToShortnames.entrySet() .stream() @@ -431,8 +433,10 @@ public Optional getFuturesShortname(@Nullable String contract) { * For example for {@code MXI-6.21}, {@code MMM1}, {@code MXI-6.21M170621CA3000} and {@code MM3000BF1} returns "MM". * Returns empty optional if argument is not futures or optional. */ - public Optional getContractGroup(String contract) { - if (isFuturesShortname(contract) || isOptionShortname(contract)) { + public Optional getContractGroup(@Nullable String contract) { + if (isNull(contract)) { + return empty(); + } else if (isFuturesShortname(contract) || isOptionShortname(contract)) { String shortname = contract.substring(0, contract.indexOf('-')); return Optional.ofNullable(shortnameToCodes.get(shortname)); } else if (isFuturesCode(contract) || isOptionCode(contract)) { diff --git a/src/main/java/ru/investbook/service/moex/MoexIssClient.java b/src/main/java/ru/investbook/service/moex/MoexIssClient.java index 4475021c..60bc1bd6 100644 --- a/src/main/java/ru/investbook/service/moex/MoexIssClient.java +++ b/src/main/java/ru/investbook/service/moex/MoexIssClient.java @@ -18,6 +18,7 @@ package ru.investbook.service.moex; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.spacious_team.broker.pojo.SecurityType; @@ -38,7 +39,7 @@ public interface MoexIssClient { * * @return true if Moex hasn't quotes */ - boolean isDerivativeAndExpired(String shortnameOrSecid, SecurityType securityType); + boolean isDerivativeAndExpired(@Nullable String shortnameOrSecid, SecurityType securityType); /** * @param contract option's code (moex secid) in {@code Si65000BC9}, {@code Si65000BC9D}, {@code RI180000BD1} or diff --git a/src/main/java/ru/investbook/service/moex/MoexIssClientImpl.java b/src/main/java/ru/investbook/service/moex/MoexIssClientImpl.java index aca1cac9..72d84e6a 100644 --- a/src/main/java/ru/investbook/service/moex/MoexIssClientImpl.java +++ b/src/main/java/ru/investbook/service/moex/MoexIssClientImpl.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.spacious_team.broker.pojo.SecurityQuote.SecurityQuoteBuilder; import org.spacious_team.broker.pojo.SecurityType; @@ -173,7 +174,7 @@ public Optional getQuote(String moexSecId, MoexMarketDescription return quote; } - public boolean isDerivativeAndExpired(String shortnameOrSecid, SecurityType securityType) { + public boolean isDerivativeAndExpired(@Nullable String shortnameOrSecid, SecurityType securityType) { try { if (securityType == DERIVATIVE && shortnameOrSecid != null) { int currentYear = getCurrentYear(); diff --git a/src/main/java/ru/investbook/service/moex/MoexSecurityQuoteHelper.java b/src/main/java/ru/investbook/service/moex/MoexSecurityQuoteHelper.java index 1e7bdb28..bf4647b1 100644 --- a/src/main/java/ru/investbook/service/moex/MoexSecurityQuoteHelper.java +++ b/src/main/java/ru/investbook/service/moex/MoexSecurityQuoteHelper.java @@ -92,7 +92,7 @@ static Optional parse(Map description) { } } - private static BigDecimal toBigDecimal(Object value) { + private static BigDecimal toBigDecimal(@Nullable Object value) { return BigDecimal.valueOf( Double.parseDouble( String.valueOf( diff --git a/src/main/java/ru/investbook/web/BrokerEmailReportController.java b/src/main/java/ru/investbook/web/BrokerEmailReportController.java index fb0d1477..60e31f25 100644 --- a/src/main/java/ru/investbook/web/BrokerEmailReportController.java +++ b/src/main/java/ru/investbook/web/BrokerEmailReportController.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spacious_team.broker.report_parser.api.BrokerReportFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -45,7 +46,7 @@ public class BrokerEmailReportController { private final Collection brokerReportFactories; private final MailboxReportParserService mailboxReportParserService; - private MailboxDescriptor defaultMailboxDescriptor; + private volatile @MonotonicNonNull MailboxDescriptor defaultMailboxDescriptor; @GetMapping public String getPage(Model model) { @@ -64,7 +65,7 @@ public Object uploadBrokerReports(@ModelAttribute("mailboxDescriptor") MailboxDe "из почтового ящика " + mailbox.getLogin() + " на " + mailbox.getServer()); modelAndView.setViewName("success"); defaultMailboxDescriptor = mailbox; - defaultMailboxDescriptor.setPassword(null); + defaultMailboxDescriptor.setPassword(""); // do not store pass in RAM return modelAndView; } catch (Exception e) { return errorPage( diff --git a/src/main/java/ru/investbook/web/BrokerFileReportController.java b/src/main/java/ru/investbook/web/BrokerFileReportController.java index e17bd1a7..1214b279 100644 --- a/src/main/java/ru/investbook/web/BrokerFileReportController.java +++ b/src/main/java/ru/investbook/web/BrokerFileReportController.java @@ -38,6 +38,7 @@ import java.util.Objects; import java.util.concurrent.ConcurrentLinkedQueue; +import static java.util.Objects.requireNonNull; import static org.springframework.util.StringUtils.hasLength; import static ru.investbook.web.ReportControllerHelper.errorPage; import static ru.investbook.web.ReportControllerHelper.getBrokerNames; @@ -78,7 +79,7 @@ public ResponseEntity uploadBrokerReports(@RequestParam("reports") Multi private void uploadReport(MultipartFile report, String broker, Collection exceptions) { try (InputStream inputStream = report.getInputStream()) { // creates new input stream - brokerReportParserService.parseReport(inputStream, report.getOriginalFilename(), broker); + brokerReportParserService.parseReport(inputStream, requireNonNull(report.getOriginalFilename()), broker); } catch (Exception e) { exceptions.add(e); } diff --git a/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java b/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java index eac5a220..d6853d96 100644 --- a/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java @@ -43,6 +43,8 @@ import java.time.ZonedDateTime; import java.util.Optional; +import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.spacious_team.broker.pojo.CashFlowType.DERIVATIVE_PROFIT; import static org.springframework.data.domain.Sort.Order.asc; @@ -100,10 +102,10 @@ public void save(SecurityEventCashFlowModel e) { .id(e.getTaxId()) .eventType(CashFlowType.TAX) .value(e.getTax().negate()) - .currency(e.getTaxCurrency()) + .currency(requireNonNull(e.getTaxCurrency())) .build())); e.setTaxId(entity.getId()); - } else if (e.getTaxId() != null) { // taxId exists in db, but no tax value in edited version + } else if (nonNull(e.getTaxId())) { // taxId exists in db, but no tax value in edited version securityEventCashFlowRepository.deleteById(e.getTaxId()); } securityEventCashFlowRepository.flush(); diff --git a/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java b/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java index f9c0a8d9..ddd4ff58 100644 --- a/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java @@ -66,6 +66,8 @@ import java.util.concurrent.atomic.AtomicReference; import static java.lang.Math.abs; +import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.springframework.data.domain.Sort.Order.asc; import static org.springframework.data.domain.Sort.Order.desc; @@ -142,8 +144,8 @@ public void save(TransactionModel tr) { BigDecimal valueInPoints = tr.getPrice().multiply(multiplier); if (tr.hasDerivativeTickValue()) { value = valueInPoints - .multiply(tr.getPriceTickValue()) - .divide(tr.getPriceTick(), 6, RoundingMode.HALF_UP); + .multiply(requireNonNull(tr.getPriceTickValue())) + .divide(requireNonNull(tr.getPriceTick()), 6, RoundingMode.HALF_UP); } yield DerivativeTransaction.builder() .valueInPoints(valueInPoints) @@ -168,9 +170,11 @@ public void save(TransactionModel tr) { }; } - @SuppressWarnings("DataFlowIssue") + if (nonNull(tr.getId())) { // is null for new object + builder.id(tr.getId()); + } + AbstractTransaction transaction = builder - .id(tr.getId()) .tradeId(tr.getTradeId()) .portfolio(tr.getPortfolio()) .timestamp(tr.getDate().atTime(tr.getTime()).atZone(zoneId).toInstant()) @@ -294,7 +298,7 @@ private TransactionModel toTransactionModel(TransactionEntity e) { m.setPriceTick(BigDecimal.ONE); // information not stored in db, normalizing m.setPriceTickValue(value.getValue() .divide(BigDecimal.valueOf(m.getCount()), 6, RoundingMode.HALF_UP) - .divide(m.getPrice(), 6, RoundingMode.HALF_UP) + .divide(requireNonNull(m.getPrice()), 6, RoundingMode.HALF_UP) .abs()); m.setPriceTickValueCurrency(value.getCurrency()); });