diff --git a/src/main/java/io/bitsquare/BitSquare.java b/src/main/java/io/bitsquare/BitSquare.java index 2e5980fcba..a45505b3b3 100644 --- a/src/main/java/io/bitsquare/BitSquare.java +++ b/src/main/java/io/bitsquare/BitSquare.java @@ -8,13 +8,12 @@ import io.bitsquare.di.BitSquareModule; import io.bitsquare.di.GuiceFXMLLoader; import io.bitsquare.gui.NavigationItem; import io.bitsquare.gui.popups.Popups; -import io.bitsquare.locale.Localisation; +import io.bitsquare.gui.util.Profiler; import io.bitsquare.msg.MessageFacade; import io.bitsquare.settings.Settings; import io.bitsquare.storage.Persistence; import io.bitsquare.user.User; import io.bitsquare.util.AWTSystemTray; -import io.bitsquare.util.FileUtil; import io.bitsquare.util.StorageDirectory; import java.io.File; import java.io.IOException; @@ -47,6 +46,8 @@ public class BitSquare extends Application public static void main(String[] args) { + Profiler.init(); + Profiler.printMsgWithTime("main called"); log.debug("Startup: main " + Arrays.asList(args).toString()); if (args != null && args.length > 0) APP_NAME = args[0]; @@ -63,15 +64,10 @@ public class BitSquare extends Application return APP_NAME; } - public static String getUID() - { - return FileUtil.getApplicationFileName(); - } - @Override public void start(Stage primaryStage) throws IOException { - log.trace("Startup: start"); + Profiler.printMsgWithTime("start called"); BitSquare.primaryStage = primaryStage; Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable))); @@ -85,7 +81,7 @@ public class BitSquare extends Application walletFacade = injector.getInstance(WalletFacade.class); messageFacade = injector.getInstance(MessageFacade.class); - log.trace("Startup: messageFacade, walletFacade inited"); + Profiler.printMsgWithTime("Startup: messageFacade, walletFacade inited"); // apply stored data final User user = injector.getInstance(User.class); @@ -99,30 +95,29 @@ public class BitSquare extends Application settings.applyPersistedSettings((Settings) persistence.read(settings.getClass().getName())); - primaryStage.setTitle("BitSquare (" + getUID() + ")"); + primaryStage.setTitle("BitSquare (" + APP_NAME + ")"); GuiceFXMLLoader.setInjector(injector); - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(NavigationItem.MAIN.getFxmlUrl()), Localisation.getResourceBundle()); + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(NavigationItem.MAIN.getFxmlUrl())); final Parent mainView = loader.load(); BorderPane rootPane = new BorderPane(); rootPane.setTop(getMenuBar()); rootPane.setCenter(mainView); - final Scene scene = new Scene(rootPane, 800, 600); - scene.getStylesheets().setAll(getClass().getResource("/io/bitsquare/gui/bitsquare.css").toExternalForm()); + final Scene scene = new Scene(rootPane, 1000, 750); + scene.getStylesheets().setAll(getClass().getResource("/io/bitsquare/gui/bitsquare.css").toExternalForm(), + getClass().getResource("/io/bitsquare/gui/validation.css").toExternalForm()); setupCloseHandlers(primaryStage, scene); primaryStage.setScene(scene); primaryStage.setMinWidth(750); primaryStage.setMinHeight(500); - primaryStage.setWidth(1000); - primaryStage.setHeight(750); primaryStage.show(); - log.debug("Startup: stage displayed"); + Profiler.printMsgWithTime("Startup: primaryStage.show"); } private void setupCloseHandlers(Stage primaryStage, Scene scene) diff --git a/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java b/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java index 7bb7fb9786..1d33b0c50b 100644 --- a/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java +++ b/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java @@ -1,36 +1,90 @@ package io.bitsquare.di; import com.google.inject.Injector; +import io.bitsquare.locale.Localisation; import java.net.URL; -import java.util.ResourceBundle; +import java.util.HashMap; +import java.util.Map; import javafx.fxml.FXMLLoader; import javafx.util.Callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Guice support for fxml controllers + * Support caching. Speed up switches between UI screens. */ -public class GuiceFXMLLoader extends FXMLLoader +public class GuiceFXMLLoader { + private static final Logger log = LoggerFactory.getLogger(GuiceFXMLLoader.class); private static Injector injector = null; + private FXMLLoader loader; + private final boolean isCached; + private final URL url; + private Item item; public static void setInjector(Injector injector) { GuiceFXMLLoader.injector = injector; } - // private static ClassLoader cachingClassLoader = new CachingClassLoader(FXMLLoader.getDefaultClassLoader()); - public GuiceFXMLLoader(URL url, ResourceBundle resourceBundle) + + // TODO maybe add more sophisticated caching strategy with removal of rarely accessed items + private static final Map cachedGUIItems = new HashMap<>(); + + public GuiceFXMLLoader(URL url) { - super(url, resourceBundle); - // might be helpful for performance, but need further profiling. has memory drawbacks - //setClassLoader(cachingClassLoader); - setupControllerFactory(); + this(url, true); } - private void setupControllerFactory() + public GuiceFXMLLoader(URL url, boolean useCaching) { - if (GuiceFXMLLoader.injector != null) + this.url = url; + + isCached = useCaching && cachedGUIItems.containsKey(url); + + if (!isCached) { - setControllerFactory(new GuiceControllerFactory(GuiceFXMLLoader.injector)); + loader = new FXMLLoader(url, Localisation.getResourceBundle()); + if (GuiceFXMLLoader.injector != null) + loader.setControllerFactory(new GuiceControllerFactory(GuiceFXMLLoader.injector)); + } + } + + @SuppressWarnings("unchecked") + public T load() throws java.io.IOException + { + if (isCached) + { + item = cachedGUIItems.get(url); + log.debug("loaded from cache " + url); + return (T) cachedGUIItems.get(url).view; + } + else + { + log.debug("load from disc " + url); + T result = loader.load(); + item = new Item(result, loader.getController()); + cachedGUIItems.put(url, item); + return result; + } + } + + @SuppressWarnings("unchecked") + public T getController() + { + return (T) item.controller; + } + + + class Item + { + final T view; + final T controller; + + Item(T view, T controller) + { + this.view = view; + this.controller = controller; } } } diff --git a/src/main/java/io/bitsquare/gui/MainController.java b/src/main/java/io/bitsquare/gui/MainController.java index 444e4e9c7d..7389842cec 100644 --- a/src/main/java/io/bitsquare/gui/MainController.java +++ b/src/main/java/io/bitsquare/gui/MainController.java @@ -10,7 +10,6 @@ import io.bitsquare.gui.market.MarketController; import io.bitsquare.gui.orders.OrdersController; import io.bitsquare.gui.util.Icons; import io.bitsquare.gui.util.Transitions; -import io.bitsquare.locale.Localisation; import io.bitsquare.msg.BootstrapListener; import io.bitsquare.msg.MessageFacade; import io.bitsquare.storage.Persistence; @@ -21,7 +20,6 @@ import io.bitsquare.util.AWTSystemTray; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; -import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -53,7 +51,7 @@ public class MainController implements Initializable, NavigationController private final ToggleGroup toggleGroup = new ToggleGroup(); - private ChildController childController; + private ChildController controller; private ToggleButton prevToggleButton; private Image prevToggleButtonIcon; private ToggleButton buyButton, sellButton, homeButton, msgButton, ordersButton, fundsButton, settingsButton; @@ -108,75 +106,6 @@ public class MainController implements Initializable, NavigationController @Override public void initialize(URL url, ResourceBundle rb) - { - Platform.runLater(this::init); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Interface implementation: NavigationController - /////////////////////////////////////////////////////////////////////////////////////////// - - - @Override - public ChildController navigateToView(NavigationItem navigationItem) - { - switch (navigationItem) - { - case HOME: - homeButton.fire(); - break; - case FUNDS: - fundsButton.fire(); - break; - case MSG: - msgButton.fire(); - break; - case ORDERS: - ordersButton.fire(); - break; - case SETTINGS: - settingsButton.fire(); - break; - case SELL: - sellButton.fire(); - break; - case BUY: - buyButton.fire(); - break; - } - return childController; - } - - - private ChildController loadView(NavigationItem navigationItem) - { - if (childController != null) - { - childController.cleanup(); - } - - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), Localisation.getResourceBundle()); - try - { - final Node view = loader.load(); - contentPane.getChildren().setAll(view); - childController = loader.getController(); - childController.setNavigationController(this); - } catch (IOException e) - { - e.printStackTrace(); - log.error("Loading view failed. " + navigationItem.getFxmlUrl()); - } - return childController; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private methods - /////////////////////////////////////////////////////////////////////////////////////////// - - private void init() { messageFacade.init(new BootstrapListener() { @@ -215,6 +144,70 @@ public class MainController implements Initializable, NavigationController tradeManager.addTakeOfferRequestListener(this::onTakeOfferRequested); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Interface implementation: NavigationController + /////////////////////////////////////////////////////////////////////////////////////////// + + + @Override + public ChildController navigateToView(NavigationItem navigationItem) + { + switch (navigationItem) + { + case HOME: + homeButton.fire(); + break; + case FUNDS: + fundsButton.fire(); + break; + case MSG: + msgButton.fire(); + break; + case ORDERS: + ordersButton.fire(); + break; + case SETTINGS: + settingsButton.fire(); + break; + case SELL: + sellButton.fire(); + break; + case BUY: + buyButton.fire(); + break; + } + return controller; + } + + + private ChildController loadView(NavigationItem navigationItem) + { + if (controller != null) + { + controller.cleanup(); + } + + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl())); + try + { + final Node view = loader.load(); + contentPane.getChildren().setAll(view); + controller = loader.getController(); + controller.setNavigationController(this); + } catch (IOException e) + { + e.printStackTrace(); + log.error("Loading view failed. " + navigationItem.getFxmlUrl()); + } + return controller; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////////////////////////////////////// + private void initialisationDone() { addNavigation(); @@ -292,11 +285,11 @@ public class MainController implements Initializable, NavigationController prevToggleButtonIcon = ((ImageView) (toggleButton.getGraphic())).getImage(); ((ImageView) (toggleButton.getGraphic())).setImage(Icons.getIconImage(navigationItem.getActiveIcon())); - childController = loadView(navigationItem); + controller = loadView(navigationItem); - if (childController instanceof MarketController) + if (controller instanceof MarketController) { - ((MarketController) childController).setDirection(navigationItem == NavigationItem.BUY ? Direction.BUY : Direction.SELL); + ((MarketController) controller).setDirection(navigationItem == NavigationItem.BUY ? Direction.BUY : Direction.SELL); } persistence.write(this, "selectedNavigationItem", navigationItem); diff --git a/src/main/java/io/bitsquare/gui/arbitrators/overview/ArbitratorOverviewController.java b/src/main/java/io/bitsquare/gui/arbitrators/overview/ArbitratorOverviewController.java index 1561a5b098..50bfc8b108 100644 --- a/src/main/java/io/bitsquare/gui/arbitrators/overview/ArbitratorOverviewController.java +++ b/src/main/java/io/bitsquare/gui/arbitrators/overview/ArbitratorOverviewController.java @@ -6,7 +6,6 @@ import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationItem; import io.bitsquare.gui.arbitrators.profile.ArbitratorProfileController; import io.bitsquare.locale.LanguageUtil; -import io.bitsquare.locale.Localisation; import io.bitsquare.msg.MessageFacade; import io.bitsquare.msg.listeners.ArbitratorListener; import io.bitsquare.settings.Settings; @@ -113,7 +112,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl arbitratorProfileController.cleanup(); } - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), Localisation.getResourceBundle()); + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl())); try { final Node view = loader.load(); diff --git a/src/main/java/io/bitsquare/gui/arbitrators/registration/ArbitratorRegistrationController.java b/src/main/java/io/bitsquare/gui/arbitrators/registration/ArbitratorRegistrationController.java index f80ebc1496..c3764cf7e2 100644 --- a/src/main/java/io/bitsquare/gui/arbitrators/registration/ArbitratorRegistrationController.java +++ b/src/main/java/io/bitsquare/gui/arbitrators/registration/ArbitratorRegistrationController.java @@ -9,7 +9,6 @@ import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.arbitrators.profile.ArbitratorProfileController; import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator; -import io.bitsquare.gui.util.BitSquareConverter; import io.bitsquare.gui.util.BitSquareFormatter; import io.bitsquare.gui.util.BitSquareValidator; import io.bitsquare.gui.util.ConfidenceDisplay; @@ -475,11 +474,11 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey()); String name = nameTextField.getText(); - double maxTradeVolume = BitSquareConverter.stringToDouble(maxTradeVolumeTextField.getText()); - double passiveServiceFee = BitSquareConverter.stringToDouble(passiveServiceFeeTextField.getText()); - double minPassiveServiceFee = BitSquareConverter.stringToDouble(minPassiveServiceFeeTextField.getText()); - double arbitrationFee = BitSquareConverter.stringToDouble(arbitrationFeeTextField.getText()); - double minArbitrationFee = BitSquareConverter.stringToDouble(minArbitrationFeeTextField.getText()); + double maxTradeVolume = BitSquareFormatter.parseToDouble(maxTradeVolumeTextField.getText()); + double passiveServiceFee = BitSquareFormatter.parseToDouble(passiveServiceFeeTextField.getText()); + double minPassiveServiceFee = BitSquareFormatter.parseToDouble(minPassiveServiceFeeTextField.getText()); + double arbitrationFee = BitSquareFormatter.parseToDouble(arbitrationFeeTextField.getText()); + double minArbitrationFee = BitSquareFormatter.parseToDouble(minArbitrationFeeTextField.getText()); String webUrl = webPageTextField.getText(); String description = descriptionTextArea.getText(); diff --git a/src/main/java/io/bitsquare/gui/bitsquare.css b/src/main/java/io/bitsquare/gui/bitsquare.css index cdea68d53c..617f8bfbd1 100644 --- a/src/main/java/io/bitsquare/gui/bitsquare.css +++ b/src/main/java/io/bitsquare/gui/bitsquare.css @@ -111,7 +111,7 @@ -fx-fill: black; } -.table-view .table-row-cell:selected .table-row-cell:row-selection .table-row-cell:cell-selection .text { +.table-view .table-row-cell:selected .table-row-cell:row-selection .table-row-cell:cell-selection .text { -fx-fill: white; } @@ -204,3 +204,9 @@ .scroll-pane .corner { -fx-background-insets: 0; } + + +/* validation */ +#validation-error { + -fx-text-fill: red; +} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/components/CachingTabPane.java b/src/main/java/io/bitsquare/gui/components/CachingTabPane.java index 4aa7f857c0..06ec6858aa 100644 --- a/src/main/java/io/bitsquare/gui/components/CachingTabPane.java +++ b/src/main/java/io/bitsquare/gui/components/CachingTabPane.java @@ -4,7 +4,6 @@ import io.bitsquare.di.GuiceFXMLLoader; import io.bitsquare.gui.ChildController; import io.bitsquare.gui.Hibernate; import io.bitsquare.gui.NavigationController; -import io.bitsquare.locale.Localisation; import io.bitsquare.storage.Persistence; import java.io.IOException; import java.util.ArrayList; @@ -92,7 +91,7 @@ public class CachingTabPane extends TabPane { // load for the first time String fxmlView = tabInfoList.get(selectedTabIndex).url; - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(fxmlView), Localisation.getResourceBundle()); + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(fxmlView)); try { tabInfoList.get(selectedTabIndex).view = loader.load(); diff --git a/src/main/java/io/bitsquare/gui/components/ValidatedTextField.java b/src/main/java/io/bitsquare/gui/components/ValidatedTextField.java index 8e7d6ed7bc..7ec049a1cb 100644 --- a/src/main/java/io/bitsquare/gui/components/ValidatedTextField.java +++ b/src/main/java/io/bitsquare/gui/components/ValidatedTextField.java @@ -132,7 +132,7 @@ public class ValidatedTextField extends TextField @Override public void changed(ObservableValue ov, String t, String t1) { - if (textProperty().get().length() > maxLength.get()) + if (textProperty().get() != null && textProperty().get().length() > maxLength.get()) { setText(t); } @@ -173,7 +173,7 @@ public class ValidatedTextField extends TextField @Override protected boolean computeValue() { - return !textProperty().get().matches(mask.get()); + return (textProperty().get() != null) ? !textProperty().get().matches(mask.get()) : false; } }; } @@ -189,7 +189,7 @@ public class ValidatedTextField extends TextField @Override protected boolean computeValue() { - return textProperty().get().length() < minLength.get(); + return (textProperty().get() != null) ? textProperty().get().length() < minLength.get() : false; } }; } diff --git a/src/main/java/io/bitsquare/gui/components/ValidatingTextField.java b/src/main/java/io/bitsquare/gui/components/ValidatingTextField.java new file mode 100644 index 0000000000..5347850a0b --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/ValidatingTextField.java @@ -0,0 +1,182 @@ +package io.bitsquare.gui.components; + +import io.bitsquare.gui.util.NumberValidator; +import java.util.LinkedList; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.geometry.Insets; +import javafx.geometry.Point2D; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.effect.BlurType; +import javafx.scene.effect.DropShadow; +import javafx.scene.effect.Effect; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.stage.Window; +import org.controlsfx.control.PopOver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * TextField with validation support. Validation is executed on the Validator object. + * In case of a invalid result we display a error message with a PopOver. + * The position is derived from the textField or if set from the errorPopupLayoutReference object. + *

+ * That class implements just what we need for the moment. It is not intended as a general purpose library class. + */ +public class ValidatingTextField extends TextField +{ + private static final Logger log = LoggerFactory.getLogger(ValidatingTextField.class); + private static final Effect DEFAULT_EFFECT = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0); + + // we hold all error popups any only display the latest one + private static final LinkedList allErrorPopups = new LinkedList<>(); + + private Effect invalidEffect = DEFAULT_EFFECT; + private final BooleanProperty valid = new SimpleBooleanProperty(true); + private NumberValidator numberValidator; + private boolean validateOnFocusOut = true; + private boolean needsValidationOnFocusOut; + private PopOver popOver; + + private Region errorPopupLayoutReference; + private double errorPopOverX; + private double errorPopOverY; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + public ValidatingTextField() + { + super(); + setupListeners(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public methods + /////////////////////////////////////////////////////////////////////////////////////////// + + public void reValidate() + { + validate(getText()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Setters + /////////////////////////////////////////////////////////////////////////////////////////// + + public void setNumberValidator(NumberValidator numberValidator) + { + this.numberValidator = numberValidator; + } + + public void setErrorPopupLayoutReference(Region errorPopupLayoutReference) + { + this.errorPopupLayoutReference = errorPopupLayoutReference; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////////////////////////////////////// + + private void setupListeners() + { + this.textProperty().addListener((ov, oldValue, newValue) -> { + if (numberValidator != null) + { + if (!validateOnFocusOut) + validate(newValue); + else + needsValidationOnFocusOut = true; + } + }); + + this.focusedProperty().addListener((ov, oldValue, newValue) -> { + if (validateOnFocusOut && needsValidationOnFocusOut && !newValue && getScene().getWindow().isFocused()) + validate(getText()); + }); + + this.valid.addListener((ov, oldValue, newValue) -> applyEffect(newValue)); + } + + private void validate(String input) + { + NumberValidator.ValidationResult validationResult = numberValidator.validate(input); + valid.set(validationResult.isValid); + applyErrorMessage(validationResult); + } + + private void applyErrorMessage(NumberValidator.ValidationResult validationResult) + { + if (validationResult.isValid) + { + if (allErrorPopups.contains(popOver)) + { + allErrorPopups.remove(popOver); + popOver.hide(); + } + if (allErrorPopups.size() > 0) + { + PopOver lastPopOver = allErrorPopups.getLast(); + lastPopOver.show(getScene().getWindow()); + } + popOver = null; + } + else + { + if (allErrorPopups.size() > 0) + { + PopOver lastPopOver = allErrorPopups.getLast(); + lastPopOver.hide(); + } + + if (allErrorPopups.contains(popOver)) + { + allErrorPopups.remove(popOver); + popOver.hide(); + } + + popOver = createErrorPopOver(validationResult.errorMessage); + popOver.show(getScene().getWindow(), errorPopOverX, errorPopOverY); + allErrorPopups.add(popOver); + } + } + + private void applyEffect(boolean isValid) + { + setEffect(isValid ? null : invalidEffect); + } + + private PopOver createErrorPopOver(String errorMessage) + { + Label errorLabel = new Label(errorMessage); + errorLabel.setId("validation-error"); + errorLabel.setPadding(new Insets(0, 10, 0, 10)); + + PopOver popOver = new PopOver(errorLabel); + popOver.setAutoFix(true); + popOver.setDetachedTitle(""); + popOver.setArrowIndent(5); + Window window = getScene().getWindow(); + + Point2D point; + if (errorPopupLayoutReference == null) + { + point = localToScene(0, 0); + errorPopOverX = point.getX() + window.getX() + getWidth() + 20; + } + else + { + point = errorPopupLayoutReference.localToScene(0, 0); + errorPopOverX = point.getX() + window.getX() + errorPopupLayoutReference.getWidth() + 20; + } + errorPopOverY = point.getY() + window.getY() + Math.floor(getHeight() / 2); + return popOver; + } +} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/funds/FundsController.java b/src/main/java/io/bitsquare/gui/funds/FundsController.java index 0cdf4646a5..b5d57184ec 100644 --- a/src/main/java/io/bitsquare/gui/funds/FundsController.java +++ b/src/main/java/io/bitsquare/gui/funds/FundsController.java @@ -19,7 +19,7 @@ public class FundsController implements Initializable, ChildController, Navigati private final Persistence persistence; @FXML - private CachingTabPane tabPane; + private CachingTabPane root; /////////////////////////////////////////////////////////////////////////////////////////// @@ -40,7 +40,7 @@ public class FundsController implements Initializable, ChildController, Navigati @Override public void initialize(URL url, ResourceBundle rb) { - tabPane.initialize(this, persistence, NavigationItem.DEPOSIT.getFxmlUrl(), NavigationItem.WITHDRAWAL.getFxmlUrl(), NavigationItem.TRANSACTIONS.getFxmlUrl()); + root.initialize(this, persistence, NavigationItem.DEPOSIT.getFxmlUrl(), NavigationItem.WITHDRAWAL.getFxmlUrl(), NavigationItem.TRANSACTIONS.getFxmlUrl()); } @@ -56,7 +56,7 @@ public class FundsController implements Initializable, ChildController, Navigati @Override public void cleanup() { - tabPane.cleanup(); + root.cleanup(); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public class FundsController implements Initializable, ChildController, Navigati @Override public ChildController navigateToView(NavigationItem navigationItem) { - return tabPane.navigateToView(navigationItem.getFxmlUrl()); + return root.navigateToView(navigationItem.getFxmlUrl()); } } diff --git a/src/main/java/io/bitsquare/gui/funds/FundsView.fxml b/src/main/java/io/bitsquare/gui/funds/FundsView.fxml index ccf0f337ac..139eb3d9e9 100644 --- a/src/main/java/io/bitsquare/gui/funds/FundsView.fxml +++ b/src/main/java/io/bitsquare/gui/funds/FundsView.fxml @@ -3,8 +3,8 @@ - + diff --git a/src/main/java/io/bitsquare/gui/funds/deposit/DepositController.java b/src/main/java/io/bitsquare/gui/funds/deposit/DepositController.java index 25fcb57768..58a0faaeca 100644 --- a/src/main/java/io/bitsquare/gui/funds/deposit/DepositController.java +++ b/src/main/java/io/bitsquare/gui/funds/deposit/DepositController.java @@ -19,6 +19,7 @@ import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.VBox; import javafx.util.Callback; import javax.inject.Inject; import org.slf4j.Logger; @@ -31,12 +32,10 @@ public class DepositController implements Initializable, ChildController, Hibern private final WalletFacade walletFacade; private ObservableList addressList; - @FXML - private TableView tableView; - @FXML - private TableColumn labelColumn, addressColumn, balanceColumn, copyColumn, confidenceColumn; - @FXML - private Button addNewAddressButton; + @FXML private VBox root; + @FXML private TableView tableView; + @FXML private TableColumn labelColumn, addressColumn, balanceColumn, copyColumn, confidenceColumn; + @FXML private Button addNewAddressButton; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/main/java/io/bitsquare/gui/funds/deposit/DepositView.fxml b/src/main/java/io/bitsquare/gui/funds/deposit/DepositView.fxml index 025b877c6b..9c993efa08 100644 --- a/src/main/java/io/bitsquare/gui/funds/deposit/DepositView.fxml +++ b/src/main/java/io/bitsquare/gui/funds/deposit/DepositView.fxml @@ -4,7 +4,7 @@ - diff --git a/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsController.java b/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsController.java index d04fb93bbe..1e75b8bf03 100644 --- a/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsController.java +++ b/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsController.java @@ -15,6 +15,7 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; +import javafx.scene.layout.VBox; import javafx.util.Callback; import javax.inject.Inject; import org.slf4j.Logger; @@ -27,12 +28,10 @@ public class TransactionsController implements Initializable, ChildController, H private final WalletFacade walletFacade; private ObservableList transactionsListItems; - @FXML - private TableView tableView; - @FXML - private TableColumn dateColumn, addressColumn, amountColumn, typeColumn, confidenceColumn; - @FXML - private Button addNewAddressButton; + @FXML private VBox root; + @FXML private TableView tableView; + @FXML private TableColumn dateColumn, addressColumn, amountColumn, typeColumn, confidenceColumn; + @FXML private Button addNewAddressButton; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsView.fxml b/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsView.fxml index 75c57f43b1..64f6ec25ee 100644 --- a/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsView.fxml +++ b/src/main/java/io/bitsquare/gui/funds/transactions/TransactionsView.fxml @@ -4,7 +4,7 @@ - diff --git a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java index b65fe304ab..4573f56ef8 100644 --- a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java +++ b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java @@ -29,6 +29,7 @@ import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.VBox; import javafx.util.Callback; import javax.inject.Inject; import org.controlsfx.control.action.Action; @@ -44,14 +45,11 @@ public class WithdrawalController implements Initializable, ChildController, Hib private final WalletFacade walletFacade; private ObservableList addressList; - @FXML - private TableView tableView; - @FXML - private TableColumn labelColumn, addressColumn, balanceColumn, copyColumn, confidenceColumn; - @FXML - private Button addNewAddressButton; - @FXML - private TextField withdrawFromTextField, withdrawToTextField, amountTextField, changeAddressTextField; + @FXML private VBox root; + @FXML private TableView tableView; + @FXML private TableColumn labelColumn, addressColumn, balanceColumn, copyColumn, confidenceColumn; + @FXML private Button addNewAddressButton; + @FXML private TextField withdrawFromTextField, withdrawToTextField, amountTextField, changeAddressTextField; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalView.fxml b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalView.fxml index f8505d15a3..b69c500ec0 100644 --- a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalView.fxml +++ b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalView.fxml @@ -4,7 +4,7 @@ - + diff --git a/src/main/java/io/bitsquare/gui/home/HomeController.java b/src/main/java/io/bitsquare/gui/home/HomeController.java index cd6bcef421..2df2880455 100644 --- a/src/main/java/io/bitsquare/gui/home/HomeController.java +++ b/src/main/java/io/bitsquare/gui/home/HomeController.java @@ -6,23 +6,18 @@ import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationItem; import io.bitsquare.gui.arbitrators.registration.ArbitratorRegistrationController; -import io.bitsquare.locale.Localisation; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.fxml.Initializable; -import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.layout.Pane; import javafx.stage.Modality; import javafx.stage.Stage; public class HomeController implements Initializable, ChildController, NavigationController { - @FXML - public Pane rootContainer; private ArbitratorRegistrationController arbitratorRegistrationController; @Override @@ -54,10 +49,11 @@ public class HomeController implements Initializable, ChildController, Navigatio arbitratorRegistrationController.cleanup(); } - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), Localisation.getResourceBundle()); + // dont use caching here, cause exc. -> need to investigate and is rarely called so no caching is better + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), false); try { - final Node view = loader.load(); + final Parent view = loader.load(); arbitratorRegistrationController = loader.getController(); arbitratorRegistrationController.setNavigationController(this); @@ -72,7 +68,7 @@ public class HomeController implements Initializable, ChildController, Navigatio stage.setY(rootStage.getY() + 50); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner(rootStage); - Scene scene = new Scene((Parent) view, 800, 600); + Scene scene = new Scene(view, 800, 600); stage.setScene(scene); stage.show(); diff --git a/src/main/java/io/bitsquare/gui/market/MarketController.java b/src/main/java/io/bitsquare/gui/market/MarketController.java index 30e2f64a2c..9a3252456a 100644 --- a/src/main/java/io/bitsquare/gui/market/MarketController.java +++ b/src/main/java/io/bitsquare/gui/market/MarketController.java @@ -5,16 +5,15 @@ import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationItem; import io.bitsquare.gui.market.orderbook.OrderBookController; -import io.bitsquare.locale.Localisation; import io.bitsquare.trade.Direction; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.fxml.Initializable; +import javafx.scene.Parent; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -import javafx.scene.layout.Pane; public class MarketController implements Initializable, NavigationController, ChildController { @@ -52,10 +51,10 @@ public class MarketController implements Initializable, NavigationController, Ch return null; } - final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), Localisation.getResourceBundle()); + final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl())); try { - final Pane view = loader.load(); + final Parent view = loader.load(); ChildController childController = loader.getController(); childController.setNavigationController(this); diff --git a/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferController.java b/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferController.java index 618629a750..2f6ba67e7f 100644 --- a/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferController.java +++ b/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferController.java @@ -9,12 +9,15 @@ import io.bitsquare.gui.ChildController; import io.bitsquare.gui.Hibernate; import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationItem; +import io.bitsquare.gui.components.ValidatingTextField; import io.bitsquare.gui.components.btc.AddressTextField; import io.bitsquare.gui.components.btc.BalanceTextField; import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator; import io.bitsquare.gui.popups.Popups; -import io.bitsquare.gui.util.BitSquareConverter; import io.bitsquare.gui.util.BitSquareFormatter; +import io.bitsquare.gui.util.BtcValidator; +import io.bitsquare.gui.util.FiatValidator; +import io.bitsquare.gui.util.ValidationHelper; import io.bitsquare.locale.Localisation; import io.bitsquare.settings.Settings; import io.bitsquare.trade.Direction; @@ -36,6 +39,7 @@ import javafx.scene.control.Label; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +68,7 @@ class ViewModel final StringProperty acceptedLanguages = new SimpleStringProperty(); final StringProperty transactionId = new SimpleStringProperty(); final BooleanProperty isOfferPlacedScreen = new SimpleBooleanProperty(); - final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(); + final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(false); } public class CreateOfferController implements Initializable, ChildController, Hibernate @@ -74,33 +78,34 @@ public class CreateOfferController implements Initializable, ChildController, Hi private NavigationController navigationController; private final TradeManager tradeManager; private final WalletFacade walletFacade; - private final ViewModel viewModel = new ViewModel(); + final ViewModel viewModel = new ViewModel(); private final double collateral; private Direction direction; @FXML private AnchorPane rootContainer; @FXML private Label buyLabel, confirmationLabel, txTitleLabel, collateralLabel; - @FXML private TextField volumeTextField, amountTextField, priceTextField, totalsTextField; + + @FXML private ValidatingTextField amountTextField, minAmountTextField, priceTextField, volumeTextField; @FXML private Button placeOfferButton, closeButton; - @FXML private TextField collateralTextField, minAmountTextField, bankAccountTypeTextField, bankAccountCurrencyTextField, bankAccountCountyTextField, acceptedCountriesTextField, acceptedLanguagesTextField, + @FXML private TextField totalsTextField, collateralTextField, bankAccountTypeTextField, bankAccountCurrencyTextField, bankAccountCountyTextField, acceptedCountriesTextField, acceptedLanguagesTextField, feeLabel, transactionIdTextField; @FXML private ConfidenceProgressIndicator progressIndicator; @FXML private AddressTextField addressTextField; @FXML private BalanceTextField balanceTextField; - /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// @Inject - private CreateOfferController(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, User user) + CreateOfferController(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, User user) { this.tradeManager = tradeManager; this.walletFacade = walletFacade; this.collateral = settings.getCollateral(); + viewModel.collateralLabel.set("Collateral (" + BitSquareFormatter.formatCollateralPercent(collateral) + "):"); BankAccount bankAccount = user.getCurrentBankAccount(); if (bankAccount != null) { @@ -126,20 +131,6 @@ public class CreateOfferController implements Initializable, ChildController, Hi viewModel.amount.set(BitSquareFormatter.formatCoin(orderBookFilter.getAmount())); viewModel.minAmount.set(BitSquareFormatter.formatCoin(orderBookFilter.getAmount())); viewModel.price.set(BitSquareFormatter.formatPrice(orderBookFilter.getPrice())); - viewModel.collateralLabel.set("Collateral (" + BitSquareFormatter.formatCollateralPercent(collateral) + "):"); - - //TODO just for dev testing - if (BitSquare.fillFormsWithDummyData) - { - if (orderBookFilter.getAmount() == null) - { - amountTextField.setText("1"); - minAmountTextField.setText("0.1"); - } - - if (orderBookFilter.getPrice() == 0) - priceTextField.setText("" + (int) (499 - new Random().nextDouble() * 1000 / 100)); - } } @@ -150,15 +141,34 @@ public class CreateOfferController implements Initializable, ChildController, Hi @Override public void initialize(URL url, ResourceBundle rb) { - AddressEntry addressEntry = walletFacade.getUnusedTradeAddressInfo(); - addressTextField.setAddress(addressEntry.getAddress().toString()); + setupBindings(); + setupValidation(); - balanceTextField.setAddress(addressEntry.getAddress()); - balanceTextField.setWalletFacade(walletFacade); + //TODO + if (walletFacade.getWallet() != null) + { + AddressEntry addressEntry = walletFacade.getUnusedTradeAddressInfo(); + addressTextField.setAddress(addressEntry.getAddress().toString()); + balanceTextField.setAddress(addressEntry.getAddress()); + balanceTextField.setWalletFacade(walletFacade); + } + + + //TODO just for dev testing + if (BitSquare.fillFormsWithDummyData) + { + amountTextField.setText("1"); + minAmountTextField.setText("0.1"); + priceTextField.setText("" + (int) (499 - new Random().nextDouble() * 1000 / 100)); + } + } + + private void setupBindings() + { // setup bindings - DoubleBinding amountBinding = createDoubleBinding(() -> BitSquareConverter.stringToDouble(viewModel.amount.get()), viewModel.amount); - DoubleBinding priceBinding = createDoubleBinding(() -> BitSquareConverter.stringToDouble(viewModel.price.get()), viewModel.price); + DoubleBinding amountBinding = createDoubleBinding(() -> BitSquareFormatter.parseToDouble(viewModel.amount.get()), viewModel.amount); + DoubleBinding priceBinding = createDoubleBinding(() -> BitSquareFormatter.parseToDouble(viewModel.price.get()), viewModel.price); viewModel.volume.bind(createStringBinding(() -> BitSquareFormatter.formatVolume(amountBinding.get() * priceBinding.get()), amountBinding, priceBinding)); viewModel.collateral.bind(createStringBinding(() -> BitSquareFormatter.formatCollateralAsBtc(viewModel.amount.get(), collateral), amountBinding)); @@ -185,7 +195,6 @@ public class CreateOfferController implements Initializable, ChildController, Hi transactionIdTextField.textProperty().bind(viewModel.transactionId); placeOfferButton.visibleProperty().bind(viewModel.isOfferPlacedScreen.not()); - placeOfferButton.disableProperty().bind(viewModel.isPlaceOfferButtonDisabled); progressIndicator.visibleProperty().bind(viewModel.isOfferPlacedScreen); confirmationLabel.visibleProperty().bind(viewModel.isOfferPlacedScreen); txTitleLabel.visibleProperty().bind(viewModel.isOfferPlacedScreen); @@ -193,6 +202,25 @@ public class CreateOfferController implements Initializable, ChildController, Hi closeButton.visibleProperty().bind(viewModel.isOfferPlacedScreen); } + private void setupValidation() + { + BtcValidator amountValidator = new BtcValidator(); + amountTextField.setNumberValidator(amountValidator); + amountTextField.setErrorPopupLayoutReference((Region) amountTextField.getParent()); + priceTextField.setNumberValidator(new FiatValidator()); + priceTextField.setErrorPopupLayoutReference((Region) amountTextField.getParent()); + BtcValidator minAmountValidator = new BtcValidator(); + minAmountTextField.setNumberValidator(minAmountValidator); + + ValidationHelper.setupMinAmountInRangeOfAmountValidation(amountTextField, + minAmountTextField, + viewModel.amount, + viewModel.minAmount, + amountValidator, + minAmountValidator); + + } + /////////////////////////////////////////////////////////////////////////////////////////// // Interface implementation: ChildController @@ -229,27 +257,24 @@ public class CreateOfferController implements Initializable, ChildController, Hi /////////////////////////////////////////////////////////////////////////////////////////// // UI Handlers /////////////////////////////////////////////////////////////////////////////////////////// - + @FXML public void onPlaceOffer() { - if (inputsValid()) - { - viewModel.isPlaceOfferButtonDisabled.set(true); + viewModel.isPlaceOfferButtonDisabled.set(true); - tradeManager.requestPlaceOffer(direction, - BitSquareConverter.stringToDouble(viewModel.price.get()), - BitSquareFormatter.parseToCoin(viewModel.amount.get()), - BitSquareFormatter.parseToCoin(viewModel.minAmount.get()), - (transaction) -> { - viewModel.isOfferPlacedScreen.set(true); - viewModel.transactionId.set(transaction.getHashAsString()); - }, - errorMessage -> { - Popups.openErrorPopup("An error occurred", errorMessage); - viewModel.isPlaceOfferButtonDisabled.set(false); - }); - } + tradeManager.requestPlaceOffer(direction, + BitSquareFormatter.parseToDouble(viewModel.price.get()), + BitSquareFormatter.parseToCoin(viewModel.amount.get()), + BitSquareFormatter.parseToCoin(viewModel.minAmount.get()), + (transaction) -> { + viewModel.isOfferPlacedScreen.set(true); + viewModel.transactionId.set(transaction.getHashAsString()); + }, + errorMessage -> { + Popups.openErrorPopup("An error occurred", errorMessage); + viewModel.isPlaceOfferButtonDisabled.set(false); + }); } @FXML @@ -260,48 +285,5 @@ public class CreateOfferController implements Initializable, ChildController, Hi navigationController.navigateToView(NavigationItem.ORDER_BOOK); } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private methods - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean inputsValid() - { - //TODO - /* boolean inputFieldsValid; - double amount = BitSquareConverter.stringToDouble(viewModel.amount.get()); - double minAmount = BitSquareConverter.stringToDouble(viewModel.minAmount.get()); - double price = BitSquareConverter.stringToDouble(viewModel.price.get()); - - inputFieldsValid = price > 0 && - amount > 0 && - minAmount > 0 && - minAmount <= amount/* && - viewModel.collateral >= settings.getMinCollateral() && - viewModel.collateral <= settings.getMaxCollateral()*/ /*; - - if (!inputFieldsValid) - { - Popups.openWarningPopup("Invalid input", "Your input is invalid"); - return false; - } -*/ - /* Arbitrator arbitrator = settings.getRandomArbitrator(getAmountAsCoin()); - if (arbitrator == null) - { - Popups.openWarningPopup("No arbitrator available", "No arbitrator from your arbitrator list does match the collateral and amount value."); - return false; - }*/ -/* - if (user.getCurrentBankAccount() == null) - { - log.error("Must never happen!"); - Popups.openWarningPopup("No bank account selected", "No bank account selected."); - return false; - }*/ - - return true; - } } diff --git a/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferView.fxml b/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferView.fxml index 2216e3c8ce..08e522ef84 100644 --- a/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferView.fxml +++ b/src/main/java/io/bitsquare/gui/market/createOffer/CreateOfferView.fxml @@ -3,6 +3,7 @@ + @@ -16,16 +17,16 @@

+ * That class implements just what we need for the moment. It is not intended as a general purpose library class. + */ +public class BtcValidator extends NumberValidator +{ + private static final Logger log = LoggerFactory.getLogger(BtcValidator.class); + private ValidationResult overriddenValidationResult; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public methods + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public ValidationResult validate(String input) + { + ValidationResult result = validateIfNotEmpty(input); + if (result.isValid) + { + input = cleanInput(input); + result = validateIfNumber(input); + } + + if (result.isValid) + { + result = validateIfNotZero(input) + .and(validateIfNotNegative(input)) + .and(validateIfNotFractionalBtcValue(input)) + .and(validateIfNotExceedsMaxBtcValue(input)); + } + + if (overriddenValidationResult != null) + return overriddenValidationResult; + + return result; + } + + /** + * Used to integrate external validation (e.g. for MinAmount/Amount) + * TODO Might be improved but does the job for now... + * + * @param overriddenValidationResult + */ + public void overrideResult(ValidationResult overriddenValidationResult) + { + this.overriddenValidationResult = overriddenValidationResult; + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected methods + /////////////////////////////////////////////////////////////////////////////////////////// + + protected ValidationResult validateIfNotFractionalBtcValue(String input) + { + BigDecimal bd = new BigDecimal(input); + final BigDecimal satoshis = bd.movePointRight(8); + if (satoshis.scale() > 0) + return new ValidationResult(false, "Input results in a Bitcoin value with a fraction of the smallest unit (Satoshi).", ErrorType.FRACTIONAL_SATOSHI); + else + return new ValidationResult(true); + } + + protected ValidationResult validateIfNotExceedsMaxBtcValue(String input) + { + BigDecimal bd = new BigDecimal(input); + final BigDecimal satoshis = bd.movePointRight(8); + if (satoshis.longValue() > NetworkParameters.MAX_MONEY.longValue()) + return new ValidationResult(false, "Input larger as maximum possible Bitcoin value is not allowed.", ErrorType.EXCEEDS_MAX_BTC_VALUE); + else + return new ValidationResult(true); + } +} diff --git a/src/main/java/io/bitsquare/gui/util/FiatValidator.java b/src/main/java/io/bitsquare/gui/util/FiatValidator.java new file mode 100644 index 0000000000..25b7e6bf92 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/util/FiatValidator.java @@ -0,0 +1,67 @@ +package io.bitsquare.gui.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FiatNumberValidator for validating fiat values. + *

+ * That class implements just what we need for the moment. It is not intended as a general purpose library class. + */ +public class FiatValidator extends NumberValidator +{ + private static final Logger log = LoggerFactory.getLogger(FiatValidator.class); + + //TODO Find appropriate values - depends on currencies + public static final double MIN_FIAT_VALUE = 0.01; // usually a cent is the smallest currency unit + public static final double MAX_FIAT_VALUE = 1000000; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public methods + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public ValidationResult validate(String input) + { + ValidationResult result = validateIfNotEmpty(input); + if (result.isValid) + { + input = cleanInput(input); + result = validateIfNumber(input); + } + + if (result.isValid) + { + result = validateIfNotZero(input) + .and(validateIfNotNegative(input)) + .and(validateIfNotExceedsMinFiatValue(input)) + .and(validateIfNotExceedsMaxFiatValue(input)); + } + + return result; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected methods + /////////////////////////////////////////////////////////////////////////////////////////// + + protected ValidationResult validateIfNotExceedsMinFiatValue(String input) + { + double d = Double.parseDouble(input); + if (d < MIN_FIAT_VALUE) + return new ValidationResult(false, "Input smaller as minimum possible Fiat value is not allowed..", ErrorType.UNDERCUT_MIN_FIAT_VALUE); + else + return new ValidationResult(true); + } + + protected ValidationResult validateIfNotExceedsMaxFiatValue(String input) + { + double d = Double.parseDouble(input); + if (d > MAX_FIAT_VALUE) + return new ValidationResult(false, "Input larger as maximum possible Fiat value is not allowed.", ErrorType.EXCEEDS_MAX_FIAT_VALUE); + else + return new ValidationResult(true); + } +} diff --git a/src/main/java/io/bitsquare/gui/util/NumberValidator.java b/src/main/java/io/bitsquare/gui/util/NumberValidator.java new file mode 100644 index 0000000000..6eea4cb494 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/util/NumberValidator.java @@ -0,0 +1,127 @@ +package io.bitsquare.gui.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * NumberValidator for validating basic number values. + * Localisation not supported at the moment + * The decimal mark can be either "." or ",". Thousand separators are not supported yet, but might be added alter with Local support. + *

+ * That class implements just what we need for the moment. It is not intended as a general purpose library class. + */ +public abstract class NumberValidator +{ + private static final Logger log = LoggerFactory.getLogger(NumberValidator.class); + + /////////////////////////////////////////////////////////////////////////////////////////// + // Abstract methods + /////////////////////////////////////////////////////////////////////////////////////////// + + abstract public ValidationResult validate(String input); + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected methods + /////////////////////////////////////////////////////////////////////////////////////////// + + + protected ValidationResult validateIfNotEmpty(String input) + { + if (input == null || input.length() == 0) + return new ValidationResult(false, "Empty input is not allowed.", ErrorType.EMPTY_INPUT); + else + return new ValidationResult(true); + } + + protected String cleanInput(String input) + { + return input.replace(",", ".").trim(); + } + + protected ValidationResult validateIfNumber(String input) + { + try + { + //noinspection ResultOfMethodCallIgnored + Double.parseDouble(input); + return new ValidationResult(true); + } catch (Exception e) + { + return new ValidationResult(false, "Input is not a valid number.", ErrorType.NOT_A_NUMBER); + } + } + + protected ValidationResult validateIfNotZero(String input) + { + if (Double.parseDouble(input) == 0) + return new ValidationResult(false, "Input of 0 is not allowed.", ErrorType.ZERO_NUMBER); + else + return new ValidationResult(true); + } + + protected ValidationResult validateIfNotNegative(String input) + { + if (Double.parseDouble(input) < 0) + return new ValidationResult(false, "A negative value is not allowed.", ErrorType.NEGATIVE_NUMBER); + else + return new ValidationResult(true); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // ErrorType + /////////////////////////////////////////////////////////////////////////////////////////// + + public enum ErrorType + { + EMPTY_INPUT, + NOT_A_NUMBER, + ZERO_NUMBER, + NEGATIVE_NUMBER, + FRACTIONAL_SATOSHI, + EXCEEDS_MAX_FIAT_VALUE, UNDERCUT_MIN_FIAT_VALUE, AMOUNT_LESS_THAN_MIN_AMOUNT, MIN_AMOUNT_LARGER_THAN_MIN_AMOUNT, EXCEEDS_MAX_BTC_VALUE + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // ValidationResult + /////////////////////////////////////////////////////////////////////////////////////////// + + public static class ValidationResult + { + public final boolean isValid; + public final String errorMessage; + public final ErrorType errorType; + + public ValidationResult(boolean isValid, String errorMessage, ErrorType errorType) + { + this.isValid = isValid; + this.errorMessage = errorMessage; + this.errorType = errorType; + } + + ValidationResult(boolean isValid) + { + this(isValid, null, null); + } + + public ValidationResult and(ValidationResult next) + { + if (this.isValid) + return next; + else + return this; + } + + @Override + public String toString() + { + return "ValidationResult{" + + "isValid=" + isValid + + ", errorMessage='" + errorMessage + '\'' + + ", errorType=" + errorType + + '}'; + } + } +} diff --git a/src/main/java/io/bitsquare/gui/util/Profiler.java b/src/main/java/io/bitsquare/gui/util/Profiler.java new file mode 100644 index 0000000000..4862c97032 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/util/Profiler.java @@ -0,0 +1,50 @@ +package io.bitsquare.gui.util; + +import com.google.common.base.Stopwatch; +import java.util.concurrent.TimeUnit; +import javafx.animation.AnimationTimer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Profiler +{ + private static final Logger log = LoggerFactory.getLogger(Profiler.class); + + private static final Stopwatch globalStopwatch = Stopwatch.createStarted(); + private static final ThreadLocal threadStopwatch = ThreadLocal.withInitial(Stopwatch::createStarted); + private static final ThreadLocal last = ThreadLocal.withInitial(() -> 0L); + private static long lastCurrentTimeMillis = System.currentTimeMillis(); + private static long lastFPSTime = System.currentTimeMillis(); + private static long counter = 0; + + public static void printMsgWithTime(String msg) + { + final long elapsed = threadStopwatch.get().elapsed(TimeUnit.MILLISECONDS); + log.trace("Msg: {} elapsed: {}ms / total time:[globalStopwatch: {}ms / threadStopwatch: {}ms / currentTimeMillis: {}ms]", + msg, + elapsed - last.get(), + globalStopwatch.elapsed(TimeUnit.MILLISECONDS), + elapsed, + System.currentTimeMillis() - lastCurrentTimeMillis); + lastCurrentTimeMillis = System.currentTimeMillis(); + last.set(elapsed); + } + + public static void init() + { + AnimationTimer fpsTimer = new AnimationTimer() + { + @Override + public void handle(long l) + { + counter++; + long elapsed = (System.currentTimeMillis() - lastFPSTime); + if (elapsed > 19) + log.trace("FPS: elapsed: {}ms / FPS total counter: {}", elapsed, counter); + + lastFPSTime = System.currentTimeMillis(); + } + }; + fpsTimer.start(); + } +} diff --git a/src/main/java/io/bitsquare/gui/util/ValidationHelper.java b/src/main/java/io/bitsquare/gui/util/ValidationHelper.java new file mode 100644 index 0000000000..f3c5714d49 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/util/ValidationHelper.java @@ -0,0 +1,97 @@ +package io.bitsquare.gui.util; + +import io.bitsquare.gui.components.ValidatingTextField; +import javafx.beans.property.StringProperty; +import javafx.scene.control.TextField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper class for setting up the validation and dependencies for minAmount and Amount. + * TODO Might be improved but does the job for now... + */ +public class ValidationHelper +{ + private static final Logger log = LoggerFactory.getLogger(ValidationHelper.class); + + /** + * Handles validation between minAmount and amount fields + */ + public static void setupMinAmountInRangeOfAmountValidation(ValidatingTextField amountTextField, + ValidatingTextField minAmountTextField, + StringProperty amount, + StringProperty minAmount, + BtcValidator amountValidator, + BtcValidator minAmountValidator) + { + + + amountTextField.focusedProperty().addListener((ov, oldValue, newValue) -> { + // only on focus out and ignore focus loss from window + if (!newValue && amountTextField.getScene().getWindow().isFocused()) + validateMinAmount(amountTextField, + minAmountTextField, + amount, + minAmount, + amountValidator, + minAmountValidator, + amountTextField); + }); + + minAmountTextField.focusedProperty().addListener((ov, oldValue, newValue) -> { + // only on focus out and ignore focus loss from window + if (!newValue && minAmountTextField.getScene().getWindow().isFocused()) + validateMinAmount(amountTextField, + minAmountTextField, + amount, + minAmount, + amountValidator, + minAmountValidator, + minAmountTextField); + }); + } + + private static void validateMinAmount(ValidatingTextField amountTextField, + ValidatingTextField minAmountTextField, + StringProperty amount, + StringProperty minAmount, + BtcValidator amountValidator, + BtcValidator minAmountValidator, + TextField currentTextField) + { + amountValidator.overrideResult(null); + if (!amountValidator.validate(amount.get()).isValid) + return; + + minAmountValidator.overrideResult(null); + if (!minAmountValidator.validate(minAmount.get()).isValid) + return; + + if (currentTextField == amountTextField) + { + if (Double.parseDouble(amount.get()) < Double.parseDouble(minAmount.get())) + { + amountValidator.overrideResult(new NumberValidator.ValidationResult(false, "Amount cannot be smaller than minimum amount.", NumberValidator.ErrorType.AMOUNT_LESS_THAN_MIN_AMOUNT)); + amountTextField.reValidate(); + } + else + { + amountValidator.overrideResult(null); + minAmountTextField.reValidate(); + } + } + else if (currentTextField == minAmountTextField) + { + if (Double.parseDouble(minAmount.get()) > Double.parseDouble(amount.get())) + { + minAmountValidator.overrideResult(new NumberValidator.ValidationResult(false, "Minimum amount cannot be larger than amount.", NumberValidator.ErrorType.MIN_AMOUNT_LARGER_THAN_MIN_AMOUNT)); + minAmountTextField.reValidate(); + } + else + { + minAmountValidator.overrideResult(null); + amountTextField.reValidate(); + } + } + } +} diff --git a/src/main/java/io/bitsquare/gui/validation.css b/src/main/java/io/bitsquare/gui/validation.css new file mode 100644 index 0000000000..c07d0ce106 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/validation.css @@ -0,0 +1,22 @@ +.text-field.validation_error, .text-area.validation_error .content, .date-picker.validation_error > .text-field { + -fx-effect: innershadow(three-pass-box, red, 12 , 0.5, 1, 1); +} + + +#error-msg-item .label { + -fx-text-fill: white; +} + +#error-msg-item:focused { + -fx-background-color: #B80000; +} + +#error-msg-item:focused .label { + -fx-text-fill: white; +} + +#error-msg { + -fx-background-color: #B80000; + -fx-padding: 0; + -fx-background-radius: 4 4 4 4; +} diff --git a/src/main/java/io/bitsquare/trade/handlers/ExceptionHandler.java b/src/main/java/io/bitsquare/trade/handlers/ExceptionHandler.java index aa7bca8f46..4c12085be0 100644 --- a/src/main/java/io/bitsquare/trade/handlers/ExceptionHandler.java +++ b/src/main/java/io/bitsquare/trade/handlers/ExceptionHandler.java @@ -1,4 +1,5 @@ package io.bitsquare.trade.handlers; + /** * For reporting throwables only */ diff --git a/src/main/java/io/bitsquare/trade/protocol/offerer/VerifyAndSignContract.java b/src/main/java/io/bitsquare/trade/protocol/offerer/VerifyAndSignContract.java index 38f18afff0..d989663a2a 100644 --- a/src/main/java/io/bitsquare/trade/protocol/offerer/VerifyAndSignContract.java +++ b/src/main/java/io/bitsquare/trade/protocol/offerer/VerifyAndSignContract.java @@ -42,10 +42,10 @@ public class VerifyAndSignContract //TODO PublicKey cause problems, need to be changed to hex /*if (contractAsJson.equals(peersContractAsJson)) {*/ - log.trace("The 2 contracts as json does match"); - String signature = cryptoFacade.signContract(registrationKey, contractAsJson); - //log.trace("signature: " + signature); - resultHandler.onResult(contract, contractAsJson, signature); + log.trace("The 2 contracts as json does match"); + String signature = cryptoFacade.signContract(registrationKey, contractAsJson); + //log.trace("signature: " + signature); + resultHandler.onResult(contract, contractAsJson, signature); /* } else { diff --git a/src/main/java/io/bitsquare/util/AWTSystemTray.java b/src/main/java/io/bitsquare/util/AWTSystemTray.java index 0c145c834a..7ada023d62 100644 --- a/src/main/java/io/bitsquare/util/AWTSystemTray.java +++ b/src/main/java/io/bitsquare/util/AWTSystemTray.java @@ -31,7 +31,7 @@ public class AWTSystemTray trayIcon.setToolTip("BitSquare P2P Fiat-Bitcoin exchange"); PopupMenu popupMenu = new PopupMenu(); - MenuItem aboutItem = new MenuItem("Info about " + BitSquare.getUID()); + MenuItem aboutItem = new MenuItem("Info about " + BitSquare.getAppName()); popupMenu.add(aboutItem); popupMenu.addSeparator(); showGuiItem = new MenuItem("Close exchange window"); diff --git a/src/main/java/io/bitsquare/util/CachingClassLoader.java b/src/main/java/io/bitsquare/util/CachingClassLoader.java deleted file mode 100644 index ac6e3bdc24..0000000000 --- a/src/main/java/io/bitsquare/util/CachingClassLoader.java +++ /dev/null @@ -1,98 +0,0 @@ -package io.bitsquare.util; - -import java.io.IOException; -import java.net.URL; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -public class CachingClassLoader extends ClassLoader -{ - private final Map classes = new HashMap(); - private final ClassLoader parent; - - public CachingClassLoader(ClassLoader parent) - { - this.parent = parent; - } - - @Override - public Class loadClass(String name) throws ClassNotFoundException - { - Class c = findClass(name); - if (c == null) - { - throw new ClassNotFoundException(name); - } - return c; - } - - @Override - protected Class findClass(String className) throws ClassNotFoundException - { - if (classes.containsKey(className)) - { - // System.out.print("############## cached " + className); - Class result = classes.get(className); - return result; - } - else - { - try - { - Class result = parent.loadClass(className); - System.out.print("############## not cached " + className); - classes.put(className, result); - return result; - } catch (ClassNotFoundException ignore) - { - // System.out.println(); - classes.put(className, null); - return null; - } - } - } - - // ========= delegating methods ============= - @Override - public URL getResource(String name) - { - return parent.getResource(name); - } - - @Override - public Enumeration getResources(String name) throws IOException - { - return parent.getResources(name); - } - - @Override - public String toString() - { - return parent.toString(); - } - - @Override - public void setDefaultAssertionStatus(boolean enabled) - { - parent.setDefaultAssertionStatus(enabled); - } - - @Override - public void setPackageAssertionStatus(String packageName, boolean enabled) - { - parent.setPackageAssertionStatus(packageName, enabled); - } - - @Override - public void setClassAssertionStatus(String className, boolean enabled) - { - parent.setClassAssertionStatus(className, enabled); - } - - @Override - public void clearAssertionStatus() - { - parent.clearAssertionStatus(); - } -} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/util/FileUtil.java b/src/main/java/io/bitsquare/util/FileUtil.java index a8f233f96e..66bc0398e7 100644 --- a/src/main/java/io/bitsquare/util/FileUtil.java +++ b/src/main/java/io/bitsquare/util/FileUtil.java @@ -1,7 +1,6 @@ package io.bitsquare.util; import com.google.bitcoin.core.Utils; -import io.bitsquare.BitSquare; import java.io.File; import java.io.IOException; import org.slf4j.Logger; @@ -21,6 +20,7 @@ public class FileUtil return File.createTempFile("temp_" + prefix, null, StorageDirectory.getStorageDirectory()); } + /* public static String getApplicationFileName() { File executionRoot = new File(StorageDirectory.class.getProtectionDomain().getCodeSource().getLocation().getFile()); @@ -50,9 +50,11 @@ public class FileUtil { e.printStackTrace(); } + + // fallback use AppName return BitSquare.getAppName(); } - +*/ public static void writeTempFileToFile(File tempFile, File file) throws IOException { diff --git a/src/test/java/io/bitsquare/BitSquareTestSuite.java b/src/test/java/io/bitsquare/BitSquareTestSuite.java index 4930225b08..dc8755e547 100644 --- a/src/test/java/io/bitsquare/BitSquareTestSuite.java +++ b/src/test/java/io/bitsquare/BitSquareTestSuite.java @@ -2,7 +2,8 @@ package io.bitsquare; import io.bitsquare.btc.BtcValidatorTest; import io.bitsquare.gui.util.BitSquareConverterTest; -import io.bitsquare.gui.util.BitSquareValidatorTest; +import io.bitsquare.gui.util.BitSquareNumberValidatorTest; +import io.bitsquare.gui.util.FiatValidatorTest; import io.bitsquare.msg.P2PNodeTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -11,8 +12,10 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ BtcValidatorTest.class, BitSquareConverterTest.class, - BitSquareValidatorTest.class, - P2PNodeTest.class + BitSquareNumberValidatorTest.class, + P2PNodeTest.class, + FiatValidatorTest.class, + BtcValidatorTest.class }) public class BitSquareTestSuite diff --git a/src/test/java/io/bitsquare/gui/util/BitSquareConverterTest.java b/src/test/java/io/bitsquare/gui/util/BitSquareConverterTest.java index 6ca9aad628..ece2dd5f9e 100644 --- a/src/test/java/io/bitsquare/gui/util/BitSquareConverterTest.java +++ b/src/test/java/io/bitsquare/gui/util/BitSquareConverterTest.java @@ -11,19 +11,19 @@ public class BitSquareConverterTest public void testStringToDouble() { - assertEquals(1, BitSquareConverter.stringToDouble("1"), 0); - assertEquals(0.1, BitSquareConverter.stringToDouble("0.1"), 0); - assertEquals(0.1, BitSquareConverter.stringToDouble("0,1"), 0); - assertEquals(1, BitSquareConverter.stringToDouble("1.0"), 0); - assertEquals(1, BitSquareConverter.stringToDouble("1,0"), 0); + assertEquals(1, BitSquareFormatter.parseToDouble("1"), 0); + assertEquals(0.1, BitSquareFormatter.parseToDouble("0.1"), 0); + assertEquals(0.1, BitSquareFormatter.parseToDouble("0,1"), 0); + assertEquals(1, BitSquareFormatter.parseToDouble("1.0"), 0); + assertEquals(1, BitSquareFormatter.parseToDouble("1,0"), 0); - assertEquals(0, BitSquareConverter.stringToDouble("1,000.2"), 0); - assertEquals(0, BitSquareConverter.stringToDouble("1,000.2"), 0); - assertEquals(0, BitSquareConverter.stringToDouble(null), 0); - assertEquals(0, BitSquareConverter.stringToDouble(""), 0); - assertEquals(0, BitSquareConverter.stringToDouble(""), 0); - assertEquals(0, BitSquareConverter.stringToDouble("."), 0); - assertEquals(0, BitSquareConverter.stringToDouble(","), 0); - assertEquals(0, BitSquareConverter.stringToDouble("a"), 0); + assertEquals(0, BitSquareFormatter.parseToDouble("1,000.2"), 0); + assertEquals(0, BitSquareFormatter.parseToDouble("1,000.2"), 0); + assertEquals(0, BitSquareFormatter.parseToDouble(null), 0); + assertEquals(0, BitSquareFormatter.parseToDouble(""), 0); + assertEquals(0, BitSquareFormatter.parseToDouble(""), 0); + assertEquals(0, BitSquareFormatter.parseToDouble("."), 0); + assertEquals(0, BitSquareFormatter.parseToDouble(","), 0); + assertEquals(0, BitSquareFormatter.parseToDouble("a"), 0); } } diff --git a/src/test/java/io/bitsquare/gui/util/BitSquareValidatorTest.java b/src/test/java/io/bitsquare/gui/util/BitSquareNumberValidatorTest.java similarity index 96% rename from src/test/java/io/bitsquare/gui/util/BitSquareValidatorTest.java rename to src/test/java/io/bitsquare/gui/util/BitSquareNumberValidatorTest.java index 517493c85d..859e05b903 100644 --- a/src/test/java/io/bitsquare/gui/util/BitSquareValidatorTest.java +++ b/src/test/java/io/bitsquare/gui/util/BitSquareNumberValidatorTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -public class BitSquareValidatorTest +public class BitSquareNumberValidatorTest { @Test public void testValidateStringAsDouble() diff --git a/src/test/java/io/bitsquare/gui/util/BtcValidatorTest.java b/src/test/java/io/bitsquare/gui/util/BtcValidatorTest.java new file mode 100644 index 0000000000..d0d8224268 --- /dev/null +++ b/src/test/java/io/bitsquare/gui/util/BtcValidatorTest.java @@ -0,0 +1,35 @@ +package io.bitsquare.gui.util; + +import com.google.bitcoin.core.Coin; +import com.google.bitcoin.core.NetworkParameters; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BtcValidatorTest +{ + @Test + public void testValidate() + { + BtcValidator validator = new BtcValidator(); + NumberValidator.ValidationResult validationResult; + + // invalid cases + validationResult = validator.validate("0.000000011");// minBtc is "0.00000001" + assertFalse(validationResult.isValid); + + validationResult = validator.validate("21000001"); //maxBtc is "21000000" + assertFalse(validationResult.isValid); + + // valid cases + String minBtc = Coin.SATOSHI.toPlainString(); // "0.00000001" + validationResult = validator.validate(minBtc); + assertTrue(validationResult.isValid); + + String maxBtc = Coin.valueOf(NetworkParameters.MAX_MONEY.longValue()).toPlainString(); //"21000000" + validationResult = validator.validate(maxBtc); + assertTrue(validationResult.isValid); + } + +} diff --git a/src/test/java/io/bitsquare/gui/util/FiatValidatorTest.java b/src/test/java/io/bitsquare/gui/util/FiatValidatorTest.java new file mode 100644 index 0000000000..c904409c9d --- /dev/null +++ b/src/test/java/io/bitsquare/gui/util/FiatValidatorTest.java @@ -0,0 +1,88 @@ +package io.bitsquare.gui.util; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class FiatValidatorTest +{ + @Test + public void testValidate() + { + FiatValidator validator = new FiatValidator(); + NumberValidator.ValidationResult validationResult; + + + // invalid cases + validationResult = validator.validate(null); + assertFalse(validationResult.isValid); + + validationResult = validator.validate(""); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("0"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("-1"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("a"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("2a"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("a2"); + assertFalse(validationResult.isValid); + + // at the moment we dont support thousand separators, can be added later + validationResult = validator.validate("1,100.1"); + assertFalse(validationResult.isValid); + + // at the moment we dont support thousand separators, can be added later + validationResult = validator.validate("1.100,1"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("1.100.1"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate("1,100,1"); + assertFalse(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(FiatValidator.MIN_FIAT_VALUE - 0.0000001)); + assertFalse(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(FiatValidator.MAX_FIAT_VALUE + 0.0000001)); + assertFalse(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(Double.MIN_VALUE)); + assertFalse(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(Double.MAX_VALUE)); + assertFalse(validationResult.isValid); + + + // valid cases + validationResult = validator.validate("1"); + assertTrue(validationResult.isValid); + + validationResult = validator.validate("0,1"); + assertTrue(validationResult.isValid); + + validationResult = validator.validate("0.1"); + assertTrue(validationResult.isValid); + + validationResult = validator.validate(",1"); + assertTrue(validationResult.isValid); + + validationResult = validator.validate(".1"); + assertTrue(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(FiatValidator.MIN_FIAT_VALUE)); + assertTrue(validationResult.isValid); + + validationResult = validator.validate(String.valueOf(FiatValidator.MAX_FIAT_VALUE)); + assertTrue(validationResult.isValid); + } +}