From 3b94eda701c15b77107d009af5e78151cdcd8ca3 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 16 Apr 2016 14:08:39 +0200 Subject: [PATCH] Add null checks or price and volume --- .../java/io/bitsquare/trade/offer/Offer.java | 28 ++++--- .../offerer/ProcessPayDepositRequest.java | 5 +- .../markets/charts/MarketsChartsView.java | 11 ++- .../charts/MarketsChartsViewModel.java | 26 ++++--- .../statistics/MarketsStatisticViewModel.java | 15 ++-- .../main/offer/offerbook/OfferBookView.java | 13 +++- .../offer/offerbook/OfferBookViewModel.java | 18 +++-- .../offer/takeoffer/TakeOfferDataModel.java | 3 +- .../main/offer/takeoffer/TakeOfferView.java | 74 ++++++++++--------- .../overlays/windows/OfferDetailsWindow.java | 14 +++- .../closedtrades/ClosedTradesView.java | 7 +- .../portfolio/openoffer/OpenOffersView.java | 13 +++- 12 files changed, 142 insertions(+), 85 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index fd284dd968..f5b74a9b16 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -118,7 +118,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload // E.g. Buy offer with market price 400.- leads to a 360.- price. // Sell offer with market price 400.- leads to a 440.- price. private final double marketPriceMargin; - + private final long amount; private final long minAmount; private final NodeAddress offererNodeAddress; @@ -236,19 +236,27 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return getPubKeyRing().equals(keyRing.getPubKeyRing()); } + @Nullable public Fiat getVolumeByAmount(Coin amount) { - try { - return new ExchangeRate(getPrice()).coinToFiat(amount); - } catch (Throwable t) { - log.error("getVolumeByAmount failed. Error=" + t.getMessage()); - return Fiat.valueOf(currencyCode, 0); + Fiat price = getPrice(); + if (price != null && amount != null) { + try { + return new ExchangeRate(price).coinToFiat(amount); + } catch (Throwable t) { + log.error("getVolumeByAmount failed. Error=" + t.getMessage()); + return null; + } + } else { + return null; } } + @Nullable public Fiat getOfferVolume() { return getVolumeByAmount(getAmount()); } + @Nullable public Fiat getMinOfferVolume() { return getVolumeByAmount(getMinAmount()); } @@ -337,6 +345,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return pubKeyRing; } + @Nullable public Fiat getPrice() { if (useMarketBasedPrice) { checkNotNull(priceFeed, "priceFeed must not be null"); @@ -357,14 +366,13 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return Fiat.parseFiat(currencyCode, String.valueOf(targetPrice)); } catch (Exception e) { log.error("Exception at getPrice / parseToFiat: " + e.toString() + "\n" + - "We use an inaccessible price to avoid null pointers.\n" + "That case should never happen."); - return Fiat.valueOf(currencyCode, direction == Direction.BUY ? Long.MIN_VALUE : Long.MAX_VALUE); + return null; } } else { - log.warn("We don't have a market price. We use an inaccessible price to avoid null pointers.\n" + + log.warn("We don't have a market price./n" + "That case could only happen if you don't get a price feed."); - return Fiat.valueOf(currencyCode, direction == Direction.BUY ? Long.MIN_VALUE : Long.MAX_VALUE); + return null; } } else { return Fiat.valueOf(currencyCode, fiatPrice); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java index 15b42ef113..33f4cfc812 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java @@ -82,6 +82,7 @@ public class ProcessPayDepositRequest extends TradeTask { checkArgument(takersTradePrice > 0); Fiat tradePriceAsFiat = Fiat.valueOf(trade.getOffer().getCurrencyCode(), takersTradePrice); Fiat offerPriceAsFiat = trade.getOffer().getPrice(); + checkArgument(offerPriceAsFiat != null, "offerPriceAsFiat must not be null"); double factor = (double) takersTradePrice / (double) offerPriceAsFiat.value; // We allow max. 2 % difference between own offer price calculation and takers calculation. // Market price might be different at offerers and takers side so we need a bit of tolerance. @@ -95,8 +96,8 @@ public class ProcessPayDepositRequest extends TradeTask { failed(msg); } trade.setTradePrice(takersTradePrice); - - + + checkArgument(payDepositRequest.tradeAmount > 0); trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount)); diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java index 7dfed6e79e..4eb8d7e4fc 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java @@ -47,6 +47,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.util.Callback; import javafx.util.StringConverter; +import org.bitcoinj.utils.Fiat; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -184,9 +185,13 @@ public class MarketsChartsView extends ActivatableViewAndModel e.getCurrencyCode().equals(tradeCurrency.get().getCode()) && e.getDirection().equals(Offer.Direction.BUY)) .sorted((o1, o2) -> { - long a = o1.getPrice().value; - long b = o2.getPrice().value; + long a = o1.getPrice() != null ? o1.getPrice().value : 0; + long b = o2.getPrice() != null ? o2.getPrice().value : 0; if (a != b) return a < b ? 1 : -1; return 0; @@ -105,8 +106,8 @@ class MarketsChartsViewModel extends ActivatableViewModel { .filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode()) && e.getDirection().equals(Offer.Direction.SELL)) .sorted((o1, o2) -> { - long a = o1.getPrice().value; - long b = o2.getPrice().value; + long a = o1.getPrice() != null ? o1.getPrice().value : 0; + long b = o2.getPrice() != null ? o2.getPrice().value : 0; if (a != b) return a > b ? 1 : -1; return 0; @@ -120,13 +121,16 @@ class MarketsChartsViewModel extends ActivatableViewModel { data.clear(); double accumulatedAmount = 0; for (Offer offer : sortedList) { - double price = (double) offer.getPrice().value / LongMath.pow(10, offer.getPrice().smallestUnitExponent()); - double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent()); - accumulatedAmount += amount; - if (direction.equals(Offer.Direction.BUY)) - data.add(0, new XYChart.Data(price, accumulatedAmount)); - else - data.add(new XYChart.Data(price, accumulatedAmount)); + Fiat priceAsFiat = offer.getPrice(); + if (priceAsFiat != null) { + double price = (double) priceAsFiat.value / LongMath.pow(10, priceAsFiat.smallestUnitExponent()); + double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent()); + accumulatedAmount += amount; + if (direction.equals(Offer.Direction.BUY)) + data.add(0, new XYChart.Data(price, accumulatedAmount)); + else + data.add(new XYChart.Data(price, accumulatedAmount)); + } } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/statistics/MarketsStatisticViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/markets/statistics/MarketsStatisticViewModel.java index dacbfd412e..ce5d46f8a9 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/markets/statistics/MarketsStatisticViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/markets/statistics/MarketsStatisticViewModel.java @@ -82,29 +82,30 @@ class MarketsStatisticViewModel extends ActivatableViewModel { .stream() .filter(e -> e.getDirection().equals(Offer.Direction.BUY)) .sorted((o1, o2) -> { - long a = o1.getPrice().value; - long b = o2.getPrice().value; + long a = o1.getPrice() != null ? o1.getPrice().value : 0; + long b = o2.getPrice() != null ? o2.getPrice().value : 0; if (a != b) return a < b ? 1 : -1; return 0; }) .collect(Collectors.toList()); - Fiat bestBuyOfferPrice = buyOffers.isEmpty() ? null : buyOffers.get(0).getPrice(); + List sellOffers = offers .stream() .filter(e -> e.getDirection().equals(Offer.Direction.SELL)) .sorted((o1, o2) -> { - long a = o1.getPrice().value; - long b = o2.getPrice().value; + long a = o1.getPrice() != null ? o1.getPrice().value : 0; + long b = o2.getPrice() != null ? o2.getPrice().value : 0; if (a != b) return a > b ? 1 : -1; return 0; }) .collect(Collectors.toList()); - Fiat bestSellOfferPrice = sellOffers.isEmpty() ? null : sellOffers.get(0).getPrice(); Fiat spread = null; - if (bestBuyOfferPrice != null && bestSellOfferPrice != null) + Fiat bestSellOfferPrice = sellOffers.isEmpty() ? null : sellOffers.get(0).getPrice(); + Fiat bestBuyOfferPrice = buyOffers.isEmpty() ? null : buyOffers.get(0).getPrice(); + if (bestBuyOfferPrice != null && bestSellOfferPrice != null) spread = bestSellOfferPrice.subtract(bestBuyOfferPrice); Coin totalAmount = Coin.valueOf(offers.stream().mapToLong(offer -> offer.getAmount().getValue()).sum()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java index af5c3fc6cc..95c011bbe0 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java @@ -51,6 +51,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.util.Callback; import javafx.util.StringConverter; +import org.bitcoinj.utils.Fiat; import javax.inject.Inject; @@ -164,9 +165,17 @@ public class OfferBookView extends ActivatableViewAndModel o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice())); + priceColumn.setComparator((o1, o2) -> { + Fiat price1 = o1.getOffer().getPrice(); + Fiat price2 = o2.getOffer().getPrice(); + return price1 != null && price2 != null ? price1.compareTo(price2) : 0; + }); amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount())); - volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume())); + volumeColumn.setComparator((o1, o2) -> { + Fiat offerVolume1 = o1.getOffer().getOfferVolume(); + Fiat offerVolume2 = o2.getOffer().getOfferVolume(); + return offerVolume1 != null && offerVolume2 != null ? offerVolume1.compareTo(offerVolume2) : 0; + }); paymentMethodColumn.setComparator((o1, o2) -> o1.getOffer().getPaymentMethod().compareTo(o2.getOffer().getPaymentMethod())); avatarColumn.setComparator((o1, o2) -> o1.getOffer().getOwnerNodeAddress().hostName.compareTo(o2.getOffer().getOwnerNodeAddress().hostName)); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java index 102b46a66a..1660c54ba5 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java @@ -262,14 +262,18 @@ class OfferBookViewModel extends ActivatableViewModel { Offer offer = item.getOffer(); Fiat price = offer.getPrice(); - String postFix = ""; - if (offer.getUseMarketBasedPrice()) { - postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")"; + if (price != null) { + String postFix = ""; + if (offer.getUseMarketBasedPrice()) { + postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")"; + } + if (showAllTradeCurrenciesProperty.get()) + return formatter.formatPriceWithCode(price) + postFix; + else + return formatter.formatFiat(price) + postFix; + } else { + return ""; } - if (showAllTradeCurrenciesProperty.get()) - return formatter.formatPriceWithCode(price) + postFix; - else - return formatter.formatFiat(price) + postFix; } String getVolume(OfferBookListItem item) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java index 3fafaa048f..bae28afe95 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -160,7 +160,8 @@ class TakeOfferDataModel extends ActivatableDataModel { void initWithData(Offer offer) { this.offer = offer; tradePrice = offer.getPrice(); - + // we check at the view class and close in case we dont get a price + checkNotNull(tradePrice, "tradePrice must not be null"); addressEntry = walletService.getOrCreateAddressEntry(offer.getId(), AddressEntry.Context.OFFER_FUNDING); checkNotNull(addressEntry, "addressEntry must not be null"); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java index 87624d7df1..b49fc226ad 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java @@ -194,45 +194,51 @@ public class TakeOfferView extends ActivatableViewAndModel 1; + paymentAccountsLabel.setVisible(showComboBox); + paymentAccountsLabel.setManaged(showComboBox); + paymentAccountsComboBox.setVisible(showComboBox); + paymentAccountsComboBox.setManaged(showComboBox); + paymentMethodTextField.setVisible(!showComboBox); + paymentMethodTextField.setManaged(!showComboBox); + paymentMethodLabel.setVisible(!showComboBox); + paymentMethodLabel.setManaged(!showComboBox); + if (!showComboBox) + paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId())); + currencyTextField.setText(model.dataModel.getCurrencyNameAndCode()); + directionLabel.setText(model.getDirectionLabel()); + amountDescriptionLabel.setText(model.getAmountDescription()); + amountRangeTextField.setText(model.getAmountRange()); + priceTextField.setText(model.getPrice()); + priceAsPercentageTextField.setText(model.marketPriceMargin); + addressTextField.setPaymentLabel(model.getPaymentLabel()); + addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString()); } else { - imageView.setId("image-sell-large"); - directionLabel.setId("direction-icon-label-sell"); - - takeOfferButton.setId("sell-button-big"); - nextButton.setId("sell-button"); - takeOfferButton.setText("Review offer for selling bitcoin"); + new Popup().warning("You cannot take that offer as it uses a percentage price based on the " + + "market price but there is no price feed available.") + .onClose(this::close) + .show(); } - - boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1; - paymentAccountsLabel.setVisible(showComboBox); - paymentAccountsLabel.setManaged(showComboBox); - paymentAccountsComboBox.setVisible(showComboBox); - paymentAccountsComboBox.setManaged(showComboBox); - paymentMethodTextField.setVisible(!showComboBox); - paymentMethodTextField.setManaged(!showComboBox); - paymentMethodLabel.setVisible(!showComboBox); - paymentMethodLabel.setManaged(!showComboBox); - if (!showComboBox) - paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId())); - currencyTextField.setText(model.dataModel.getCurrencyNameAndCode()); - directionLabel.setText(model.getDirectionLabel()); - amountDescriptionLabel.setText(model.getAmountDescription()); - amountRangeTextField.setText(model.getAmountRange()); - priceTextField.setText(model.getPrice()); - priceAsPercentageTextField.setText(model.marketPriceMargin); - addressTextField.setPaymentLabel(model.getPaymentLabel()); - addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString()); } public void setCloseHandler(OfferView.CloseHandler closeHandler) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java index 4a8ede823c..8d33edf583 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java @@ -175,11 +175,17 @@ public class OfferDetailsWindow extends Overlay { addLabelTextField(gridPane, ++rowIndex, CurrencyUtil.getNameByCode(offer.getCurrencyCode()) + " amount" + fiatDirectionInfo, formatter.formatFiatWithCode(offer.getVolumeByAmount(offer.getAmount()))); } - if (takeOfferHandlerOptional.isPresent()) + if (takeOfferHandlerOptional.isPresent()) { addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(tradePrice) + " " + offer.getCurrencyCode() + "/" + "BTC"); - else - addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC"); - + } else { + Fiat price = offer.getPrice(); + if (offer.getUseMarketBasedPrice()) { + addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(price) + + " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")"); + } else { + addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(price)); + } + } if (offer.isMyOffer(keyRing) && user.getPaymentAccount(offer.getOffererPaymentAccountId()) != null) addLabelTextField(gridPane, ++rowIndex, "Payment account:", user.getPaymentAccount(offer.getOffererPaymentAccountId()).getAccountName()); else diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java index 969f99ac04..f430f733b0 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java @@ -85,8 +85,11 @@ public class ClosedTradesView extends ActivatableViewAndModel { if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersView.java index fed2e45c82..396b02bb22 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersView.java @@ -34,6 +34,7 @@ import javafx.scene.control.*; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.util.Callback; +import org.bitcoinj.utils.Fiat; import javax.inject.Inject; @@ -72,8 +73,16 @@ public class OpenOffersView extends ActivatableViewAndModel o1.getOffer().getId().compareTo(o2.getOffer().getId())); directionColumn.setComparator((o1, o2) -> o1.getOffer().getDirection().compareTo(o2.getOffer().getDirection())); amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount())); - priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice())); - volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume())); + priceColumn.setComparator((o1, o2) -> { + Fiat price1 = o1.getOffer().getPrice(); + Fiat price2 = o2.getOffer().getPrice(); + return price1 != null && price2 != null ? price1.compareTo(price2) : 0; + }); + volumeColumn.setComparator((o1, o2) -> { + Fiat offerVolume1 = o1.getOffer().getOfferVolume(); + Fiat offerVolume2 = o2.getOffer().getOfferVolume(); + return offerVolume1 != null && offerVolume2 != null ? offerVolume1.compareTo(offerVolume2) : 0; + }); dateColumn.setComparator((o1, o2) -> o1.getOffer().getDate().compareTo(o2.getOffer().getDate())); dateColumn.setSortType(TableColumn.SortType.DESCENDING);