mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Merge pull request #1449 from cbeams/isolate-desktop
Isolate 'gui' module and rename to 'bisq-desktop'
This commit is contained in:
commit
83a6e74a5a
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,4 +27,3 @@ build
|
||||
desktop.ini
|
||||
*/target/*
|
||||
*.class
|
||||
/gui/deploy/*
|
||||
|
@ -1,7 +0,0 @@
|
||||
# This file determines which @bisq-network/exchange-maintainers get suggested
|
||||
# to review each pull request submitted to this repository. For more details,
|
||||
# see https://help.github.com/articles/about-codeowners/.
|
||||
|
||||
* @ManfredKarrer
|
||||
|
||||
/gui/* @ripcurlx
|
173
common/pom.xml
173
common/pom.xml
@ -1,173 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>io.bisq.exchange</groupId>
|
||||
<version>-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
<properties>
|
||||
<protobuf.version>3.3.0</protobuf.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>windows</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<protobuf.classifier>windows-x86_64</protobuf.classifier>
|
||||
<protobuf.exe>protoc.exe</protobuf.exe>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>unix</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>Linux</name>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<protobuf.classifier>linux-x86_64</protobuf.classifier>
|
||||
<protobuf.exe>protoc</protobuf.exe>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>macos</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<protobuf.classifier>osx-x86_64</protobuf.classifier>
|
||||
<protobuf.exe>protoc</protobuf.exe>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- unpack correct protobuf exe for current os -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-protoc</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protoc</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
<classifier>${protobuf.classifier}</classifier>
|
||||
<type>exe</type>
|
||||
<overWrite>true</overWrite>
|
||||
<destFileName>${protobuf.exe}</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>set-permissions</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<chmod file="${project.build.directory}/dependency/${protobuf.exe}" perm="u+rx"/>
|
||||
<echo message="permissions changed for ${project.build.directory}/dependency/${protobuf.exe}"/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Run protobuf https://www.xolstice.org/protobuf-maven-plugin/usage.html -->
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<configuration>
|
||||
<protocExecutable>${project.build.directory}/dependency/${protobuf.exe}</protocExecutable>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
<!-- Only used for the JsonFormat class, can otherwise be put in scope 'test' -->
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java-util</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- used only for reading json file for trade statistic migration
|
||||
can be removed later -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,67 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// Helps configure listener objects that are run by the `UserThread` each second
|
||||
// and can do per second, per minute and delayed second actions.
|
||||
public class Clock {
|
||||
private static final Logger log = LoggerFactory.getLogger(Clock.class);
|
||||
|
||||
public static final int IDLE_TOLERANCE = 20000;
|
||||
|
||||
public interface Listener {
|
||||
void onSecondTick();
|
||||
|
||||
void onMinuteTick();
|
||||
|
||||
void onMissedSecondTick(long missed);
|
||||
}
|
||||
|
||||
private Timer timer;
|
||||
private final List<Listener> listeners = new LinkedList<>();
|
||||
private long counter = 0;
|
||||
private long lastSecondTick;
|
||||
|
||||
public Clock() {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (timer == null) {
|
||||
lastSecondTick = System.currentTimeMillis();
|
||||
timer = UserThread.runPeriodically(() -> {
|
||||
listeners.stream().forEach(Listener::onSecondTick);
|
||||
counter++;
|
||||
if (counter >= 60) {
|
||||
counter = 0;
|
||||
listeners.stream().forEach(Listener::onMinuteTick);
|
||||
}
|
||||
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
long diff = currentTimeMillis - lastSecondTick;
|
||||
if (diff > 1000)
|
||||
listeners.stream().forEach(listener -> listener.onMissedSecondTick(diff - 1000));
|
||||
|
||||
lastSecondTick = currentTimeMillis;
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
public class CommonOptionKeys {
|
||||
public static final String LOG_LEVEL_KEY = "logLevel";
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
/**
|
||||
* Interface for the outside envelope object sent over the network or persisted to disc.
|
||||
*/
|
||||
public interface Envelope extends Proto {
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* We simulate a global frame rate timer similar to FXTimer to avoid creation of threads for each timer call.
|
||||
* Used only in headless apps like the seed node.
|
||||
*/
|
||||
public class FrameRateTimer implements Timer, Runnable {
|
||||
private final Logger log = LoggerFactory.getLogger(FrameRateTimer.class);
|
||||
|
||||
private long interval;
|
||||
private Runnable runnable;
|
||||
private long startTs;
|
||||
private boolean isPeriodically;
|
||||
private final String uid = UUID.randomUUID().toString();
|
||||
private volatile boolean stopped;
|
||||
|
||||
public FrameRateTimer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!stopped) {
|
||||
try {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
if ((currentTimeMillis - startTs) >= interval) {
|
||||
runnable.run();
|
||||
if (isPeriodically)
|
||||
startTs = currentTimeMillis;
|
||||
else
|
||||
stop();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error(t.getMessage());
|
||||
t.printStackTrace();
|
||||
stop();
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timer runLater(Duration delay, Runnable runnable) {
|
||||
this.interval = delay.toMillis();
|
||||
this.runnable = runnable;
|
||||
startTs = System.currentTimeMillis();
|
||||
MasterTimer.addListener(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timer runPeriodically(Duration interval, Runnable runnable) {
|
||||
this.interval = interval.toMillis();
|
||||
isPeriodically = true;
|
||||
this.runnable = runnable;
|
||||
startTs = System.currentTimeMillis();
|
||||
MasterTimer.addListener(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
MasterTimer.removeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof FrameRateTimer)) return false;
|
||||
|
||||
FrameRateTimer that = (FrameRateTimer) o;
|
||||
|
||||
return !(uid != null ? !uid.equals(that.uid) : that.uid != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uid != null ? uid.hashCode() : 0;
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common;
|
||||
|
||||
import io.bisq.common.locale.TradeCurrency;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class GlobalSettings {
|
||||
private static boolean useAnimations = true;
|
||||
private static Locale locale = Locale.getDefault();
|
||||
private static final ObjectProperty<Locale> localeProperty = new SimpleObjectProperty<>(locale);
|
||||
private static TradeCurrency defaultTradeCurrency;
|
||||
private static String btcDenomination;
|
||||
|
||||
|
||||
public static void setLocale(Locale locale) {
|
||||
GlobalSettings.locale = locale;
|
||||
localeProperty.set(locale);
|
||||
}
|
||||
|
||||
public static void setUseAnimations(boolean useAnimations) {
|
||||
GlobalSettings.useAnimations = useAnimations;
|
||||
}
|
||||
|
||||
public static void setDefaultTradeCurrency(TradeCurrency fiatCurrency) {
|
||||
GlobalSettings.defaultTradeCurrency = fiatCurrency;
|
||||
}
|
||||
|
||||
|
||||
public static void setBtcDenomination(String btcDenomination) {
|
||||
GlobalSettings.btcDenomination = btcDenomination;
|
||||
}
|
||||
|
||||
public static TradeCurrency getDefaultTradeCurrency() {
|
||||
return defaultTradeCurrency;
|
||||
}
|
||||
|
||||
public static String getBtcDenomination() {
|
||||
return btcDenomination;
|
||||
}
|
||||
|
||||
public static ReadOnlyObjectProperty<Locale> localeProperty() {
|
||||
return localeProperty;
|
||||
}
|
||||
|
||||
public static boolean getUseAnimations() {
|
||||
return useAnimations;
|
||||
}
|
||||
|
||||
public static Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
// Runs all listener objects periodically in a short interval.
|
||||
public class MasterTimer {
|
||||
private final static Logger log = LoggerFactory.getLogger(MasterTimer.class);
|
||||
private static final java.util.Timer timer = new java.util.Timer();
|
||||
// frame rate of 60 fps is about 16 ms but we don't need such a short interval, 100 ms should be good enough
|
||||
public static final long FRAME_INTERVAL_MS = 100;
|
||||
|
||||
static {
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
UserThread.execute(() -> listeners.stream().forEach(Runnable::run));
|
||||
}
|
||||
}, FRAME_INTERVAL_MS, FRAME_INTERVAL_MS);
|
||||
}
|
||||
|
||||
private static final Set<Runnable> listeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
public static void addListener(Runnable runnable) {
|
||||
listeners.add(runnable);
|
||||
}
|
||||
|
||||
public static void removeListener(Runnable runnable) {
|
||||
listeners.remove(runnable);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
/**
|
||||
* Interface for objects used inside an Envelope or other Payloads.
|
||||
*/
|
||||
public interface Payload extends Proto {
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
/**
|
||||
* Base interface for Envelope and Payload.
|
||||
*/
|
||||
public interface Proto {
|
||||
Message toProtoMessage();
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package io.bisq.common;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public interface Timer {
|
||||
Timer runLater(java.time.Duration delay, Runnable action);
|
||||
|
||||
Timer runPeriodically(Duration interval, Runnable runnable);
|
||||
|
||||
void stop();
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.Duration;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// Helps run delayed and periodic actions in the caller thread.
|
||||
public class UserThread {
|
||||
private static final Logger log = LoggerFactory.getLogger(UserThread.class);
|
||||
private static Class<? extends Timer> timerClass;
|
||||
|
||||
public static Executor getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public static void setExecutor(Executor executor) {
|
||||
UserThread.executor = executor;
|
||||
}
|
||||
|
||||
public static void setTimerClass(Class<? extends Timer> timerClass) {
|
||||
UserThread.timerClass = timerClass;
|
||||
}
|
||||
|
||||
static {
|
||||
// If not defined we use same thread as caller thread
|
||||
executor = MoreExecutors.directExecutor();
|
||||
timerClass = FrameRateTimer.class;
|
||||
}
|
||||
|
||||
private static Executor executor;
|
||||
|
||||
public static void execute(Runnable command) {
|
||||
UserThread.executor.execute(command);
|
||||
}
|
||||
|
||||
|
||||
// Prefer FxTimer if a delay is needed in a JavaFx class (gui module)
|
||||
public static Timer runAfterRandomDelay(Runnable runnable, long minDelayInSec, long maxDelayInSec) {
|
||||
return UserThread.runAfterRandomDelay(runnable, minDelayInSec, maxDelayInSec, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static Timer runAfterRandomDelay(Runnable runnable, long minDelay, long maxDelay, TimeUnit timeUnit) {
|
||||
return UserThread.runAfter(runnable, new Random().nextInt((int) (maxDelay - minDelay)) + minDelay, timeUnit);
|
||||
}
|
||||
|
||||
public static Timer runAfter(Runnable runnable, long delayInSec) {
|
||||
return UserThread.runAfter(runnable, delayInSec, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static Timer runAfter(Runnable runnable, long delay, TimeUnit timeUnit) {
|
||||
return getTimer().runLater(Duration.ofMillis(timeUnit.toMillis(delay)), runnable);
|
||||
}
|
||||
|
||||
public static Timer runPeriodically(Runnable runnable, long intervalInSec) {
|
||||
return UserThread.runPeriodically(runnable, intervalInSec, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static Timer runPeriodically(Runnable runnable, long interval, TimeUnit timeUnit) {
|
||||
return getTimer().runPeriodically(Duration.ofMillis(timeUnit.toMillis(interval)), runnable);
|
||||
}
|
||||
|
||||
private static Timer getTimer() {
|
||||
try {
|
||||
return timerClass.getDeclaredConstructor().newInstance();
|
||||
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||
String message = "Could not instantiate timer bsTimerClass=" + timerClass;
|
||||
log.error(message);
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.app;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Injector;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AppModule extends AbstractModule {
|
||||
protected final Environment environment;
|
||||
|
||||
private final List<AppModule> modules = new ArrayList<>();
|
||||
|
||||
protected AppModule(Environment environment) {
|
||||
Preconditions.checkNotNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
protected void install(AppModule module) {
|
||||
super.install(module);
|
||||
modules.add(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close any instances this module is responsible for and recursively close any
|
||||
* sub-modules installed via {@link #install(AppModule)}. This method
|
||||
* must be called manually, e.g. at the end of a main() method or in the stop() method
|
||||
* of a JavaFX Application; alternatively it may be registered as a JVM shutdown hook.
|
||||
*
|
||||
* @param injector the Injector originally initialized with this module
|
||||
* @see #doClose(com.google.inject.Injector)
|
||||
*/
|
||||
public final void close(Injector injector) {
|
||||
modules.forEach(module -> module.close(injector));
|
||||
doClose(injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually perform closing of any instances this module is responsible for. Called by
|
||||
* {@link #close(Injector)}.
|
||||
*
|
||||
* @param injector the Injector originally initialized with this module
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "EmptyMethod", "UnusedParameters"})
|
||||
protected void doClose(Injector injector) {
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package io.bisq.common.app;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Capabilities {
|
||||
// We can define here special features the client is supporting.
|
||||
// Useful for updates to new versions where a new data type would break backwards compatibility or to
|
||||
// limit a node to certain behaviour and roles like the seed nodes.
|
||||
// We don't use the Enum in any serialized data, as changes in the enum would break backwards compatibility. We use the ordinal integer instead.
|
||||
// Sequence in the enum must not be changed (append only).
|
||||
public enum Capability {
|
||||
TRADE_STATISTICS,
|
||||
TRADE_STATISTICS_2,
|
||||
ACCOUNT_AGE_WITNESS,
|
||||
SEED_NODE,
|
||||
DAO_FULL_NODE,
|
||||
COMP_REQUEST
|
||||
}
|
||||
|
||||
// Application need to set supported capabilities at startup
|
||||
@Getter
|
||||
@Setter
|
||||
private static ArrayList<Integer> supportedCapabilities = new ArrayList<>();
|
||||
|
||||
public static boolean isCapabilitySupported(final List<Integer> requiredItems, final List<Integer> supportedItems) {
|
||||
if (requiredItems != null && !requiredItems.isEmpty()) {
|
||||
if (supportedItems != null && !supportedItems.isEmpty()) {
|
||||
List<Integer> matches = new ArrayList<>();
|
||||
for (int requiredItem : requiredItems) {
|
||||
matches.addAll(supportedItems.stream()
|
||||
.filter(supportedItem -> requiredItem == supportedItem)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
return matches.size() == requiredItems.size();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package io.bisq.common.app;
|
||||
|
||||
public class DevEnv {
|
||||
// Was used for P2P network stress test to adjust several setting for the tests (e.g. use lower btc fees for offers,..)
|
||||
public static final boolean STRESS_TEST_MODE = false;
|
||||
|
||||
// The UI got set the private dev key so the developer does not need to do anything and can test those features.
|
||||
// Features: Arbitration registration (alt+R at account), Alert/Update (alt+m), private message to a
|
||||
// peer (click user icon and alt+r), filter/block offers by various data like offer ID (cmd + f).
|
||||
// The user can set a program argument to ignore all of those privileged network_messages. They are intended for
|
||||
// emergency cases only (beside update message and arbitrator registration).
|
||||
public static final String DEV_PRIVILEGE_PUB_KEY = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
|
||||
public static final String DEV_PRIVILEGE_PRIV_KEY = "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a";
|
||||
|
||||
// If set to true we ignore several UI behavior like confirmation popups as well dummy accounts are created and
|
||||
// offers are filled with default values. Intended to make dev testing faster.
|
||||
@SuppressWarnings("PointlessBooleanExpression")
|
||||
private static boolean devMode = false;
|
||||
|
||||
public static boolean isDevMode() {
|
||||
return devMode;
|
||||
}
|
||||
|
||||
public static void setDevMode(boolean devMode) {
|
||||
DevEnv.devMode = devMode;
|
||||
}
|
||||
|
||||
public static final boolean DAO_PHASE2_ACTIVATED = false;
|
||||
public static final boolean DAO_TRADING_ACTIVATED = false;
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.app;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
|
||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
|
||||
import ch.qos.logback.core.util.FileSize;
|
||||
import io.bisq.common.util.Profiler;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class Log {
|
||||
private static Logger logbackLogger;
|
||||
|
||||
public static void setLevel(Level logLevel) {
|
||||
logbackLogger.setLevel(logLevel);
|
||||
}
|
||||
|
||||
public static void setup(String fileName) {
|
||||
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
RollingFileAppender appender = new RollingFileAppender();
|
||||
appender.setContext(loggerContext);
|
||||
appender.setFile(fileName + ".log");
|
||||
|
||||
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
|
||||
rollingPolicy.setContext(loggerContext);
|
||||
rollingPolicy.setParent(appender);
|
||||
rollingPolicy.setFileNamePattern(fileName + "_%i.log");
|
||||
rollingPolicy.setMinIndex(1);
|
||||
rollingPolicy.setMaxIndex(10);
|
||||
rollingPolicy.start();
|
||||
|
||||
SizeBasedTriggeringPolicy triggeringPolicy = new SizeBasedTriggeringPolicy();
|
||||
triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB"));
|
||||
triggeringPolicy.start();
|
||||
|
||||
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
|
||||
encoder.setContext(loggerContext);
|
||||
encoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n");
|
||||
encoder.start();
|
||||
|
||||
//noinspection unchecked
|
||||
appender.setEncoder(encoder);
|
||||
appender.setRollingPolicy(rollingPolicy);
|
||||
//noinspection unchecked
|
||||
appender.setTriggeringPolicy(triggeringPolicy);
|
||||
appender.start();
|
||||
|
||||
logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||
//noinspection unchecked
|
||||
logbackLogger.addAppender(appender);
|
||||
logbackLogger.setLevel(Level.INFO);
|
||||
|
||||
// log errors in separate file
|
||||
// not working as expected still.... damn logback...
|
||||
/* FileAppender errorAppender = new FileAppender();
|
||||
errorAppender.setEncoder(encoder);
|
||||
errorAppender.setName("Error");
|
||||
errorAppender.setContext(loggerContext);
|
||||
errorAppender.setFile(fileName + "_error.log");
|
||||
LevelFilter levelFilter = new LevelFilter();
|
||||
levelFilter.setLevel(Level.ERROR);
|
||||
levelFilter.setOnMatch(FilterReply.ACCEPT);
|
||||
levelFilter.setOnMismatch(FilterReply.DENY);
|
||||
levelFilter.start();
|
||||
errorAppender.addFilter(levelFilter);
|
||||
errorAppender.start();
|
||||
logbackLogger.addAppender(errorAppender);*/
|
||||
}
|
||||
|
||||
public static void traceCall() {
|
||||
if (LoggerFactory.getLogger(Log.class).isTraceEnabled()) {
|
||||
StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1];
|
||||
String methodName = stackTraceElement.getMethodName();
|
||||
if (methodName.equals("<init>"))
|
||||
methodName = "Constructor ";
|
||||
String className = stackTraceElement.getClassName();
|
||||
LoggerFactory.getLogger(className).trace("Called: {}", methodName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void traceCall(String message) {
|
||||
if (LoggerFactory.getLogger(Log.class).isTraceEnabled()) {
|
||||
StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1];
|
||||
String methodName = stackTraceElement.getMethodName();
|
||||
if (methodName.equals("<init>"))
|
||||
methodName = "Constructor ";
|
||||
String className = stackTraceElement.getClassName();
|
||||
LoggerFactory.getLogger(className).trace("Called: {} [{}]", methodName, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void logIfStressTests(String msg) {
|
||||
if (DevEnv.STRESS_TEST_MODE)
|
||||
System.err.println(new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()) +
|
||||
" - " + msg +
|
||||
" / Memory(MB): " + Profiler.getUsedMemoryInMB());
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.app;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
@Slf4j
|
||||
public class Version {
|
||||
// The application versions
|
||||
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
|
||||
// Therefore all sub versions start again with 1
|
||||
// We use semantic versioning with major, minor and patch
|
||||
public static final String VERSION = "0.6.7";
|
||||
|
||||
public static int getMajorVersion(String version) {
|
||||
return getSubVersion(version, 0);
|
||||
}
|
||||
|
||||
public static int getMinorVersion(String version) {
|
||||
return getSubVersion(version, 1);
|
||||
}
|
||||
|
||||
public static int getPatchVersion(String version) {
|
||||
return getSubVersion(version, 2);
|
||||
}
|
||||
|
||||
public static boolean isNewVersion(String newVersion) {
|
||||
return isNewVersion(newVersion, VERSION);
|
||||
}
|
||||
|
||||
static boolean isNewVersion(String newVersion, String currentVersion) {
|
||||
if (newVersion.equals(currentVersion))
|
||||
return false;
|
||||
else if (getMajorVersion(newVersion) > getMajorVersion(currentVersion))
|
||||
return true;
|
||||
else if (getMajorVersion(newVersion) < getMajorVersion(currentVersion))
|
||||
return false;
|
||||
else if (getMinorVersion(newVersion) > getMinorVersion(currentVersion))
|
||||
return true;
|
||||
else if (getMinorVersion(newVersion) < getMinorVersion(currentVersion))
|
||||
return false;
|
||||
else if (getPatchVersion(newVersion) > getPatchVersion(currentVersion))
|
||||
return true;
|
||||
else if (getPatchVersion(newVersion) < getPatchVersion(currentVersion))
|
||||
return false;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getSubVersion(String version, int index) {
|
||||
final String[] split = version.split("\\.");
|
||||
checkArgument(split.length == 3, "Version number must be in semantic version format (contain 2 '.'). version=" + version);
|
||||
return Integer.parseInt(split[index]);
|
||||
}
|
||||
|
||||
// The version no. for the objects sent over the network. A change will break the serialization of old objects.
|
||||
// If objects are used for both network and database the network version is applied.
|
||||
// VERSION = 0.5.0 -> P2P_NETWORK_VERSION = 1
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static final int P2P_NETWORK_VERSION = DevEnv.STRESS_TEST_MODE ? 100 : 1;
|
||||
|
||||
// The version no. of the serialized data stored to disc. A change will break the serialization of old objects.
|
||||
// VERSION = 0.5.0 -> LOCAL_DB_VERSION = 1
|
||||
public static final int LOCAL_DB_VERSION = 1;
|
||||
|
||||
// The version no. of the current protocol. The offer holds that version.
|
||||
// A taker will check the version of the offers to see if his version is compatible.
|
||||
// VERSION = 0.5.0 -> TRADE_PROTOCOL_VERSION = 1
|
||||
public static final int TRADE_PROTOCOL_VERSION = 1;
|
||||
private static int p2pMessageVersion;
|
||||
|
||||
public static final String BSQ_TX_VERSION = "1";
|
||||
|
||||
public static int getP2PMessageVersion() {
|
||||
return p2pMessageVersion;
|
||||
}
|
||||
|
||||
// The version for the crypto network (BTC_Mainnet = 0, BTC_TestNet = 1, BTC_Regtest = 2, ...)
|
||||
private static int BASE_CURRENCY_NETWORK;
|
||||
|
||||
public static void setBaseCryptoNetworkId(int baseCryptoNetworkId) {
|
||||
BASE_CURRENCY_NETWORK = baseCryptoNetworkId;
|
||||
|
||||
// CRYPTO_NETWORK_ID is ordinal of enum. We use for changes at NETWORK_PROTOCOL_VERSION a multiplication with 10
|
||||
// to not mix up networks:
|
||||
p2pMessageVersion = BASE_CURRENCY_NETWORK + 10 * P2P_NETWORK_VERSION;
|
||||
}
|
||||
|
||||
public static int getBaseCurrencyNetwork() {
|
||||
return BASE_CURRENCY_NETWORK;
|
||||
}
|
||||
|
||||
public static void printVersion() {
|
||||
log.info("Version{" +
|
||||
"VERSION=" + VERSION +
|
||||
", P2P_NETWORK_VERSION=" + P2P_NETWORK_VERSION +
|
||||
", LOCAL_DB_VERSION=" + LOCAL_DB_VERSION +
|
||||
", TRADE_PROTOCOL_VERSION=" + TRADE_PROTOCOL_VERSION +
|
||||
", BASE_CURRENCY_NETWORK=" + BASE_CURRENCY_NETWORK +
|
||||
", getP2PNetworkId()=" + getP2PMessageVersion() +
|
||||
'}');
|
||||
}
|
||||
|
||||
public static final byte COMPENSATION_REQUEST_VERSION = (byte) 0x01;
|
||||
public static final byte VOTING_VERSION = (byte) 0x01;
|
||||
public static final byte VOTING_RELEASE_VERSION = (byte) 0x01;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.consensus;
|
||||
|
||||
/**
|
||||
* Marker interface for classes which are used in the trade contract.
|
||||
* Any change of the class fields would breaking backward compatibility.
|
||||
* If a field needs to get added it needs to be annotated with @JsonExclude (thus excluded from the contract JSON).
|
||||
* Better to use the excludeFromJsonDataMap (annotated with @JsonExclude; used in PaymentAccountPayload) to
|
||||
* add a key/value pair.
|
||||
*/
|
||||
|
||||
// TODO PubKeyRing and NodeAddress (network) are using UsedForTradeContractJson that is why it is in common module,
|
||||
// which is a bit weird... Maybe we need either rename common or split it to util and common where common is common code
|
||||
// used in network and core?
|
||||
public interface UsedForTradeContractJson {
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
public class CryptoException extends Exception {
|
||||
public CryptoException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CryptoException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CryptoException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
@Slf4j
|
||||
public class CryptoUtils {
|
||||
public static String pubKeyToString(PublicKey publicKey) {
|
||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
||||
}
|
||||
|
||||
public static byte[] getRandomBytes(int size) {
|
||||
byte[] bytes = new byte[size];
|
||||
new SecureRandom().nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
@ -1,237 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import io.bisq.common.util.Utilities;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
// TODO is Hmac needed/make sense?
|
||||
public class Encryption {
|
||||
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
|
||||
|
||||
public static final String ASYM_KEY_ALGO = "RSA";
|
||||
private static final String ASYM_CIPHER = "RSA/None/OAEPWithSHA256AndMGF1Padding";
|
||||
|
||||
private static final String SYM_KEY_ALGO = "AES";
|
||||
private static final String SYM_CIPHER = "AES";
|
||||
|
||||
private static final String HMAC = "HmacSHA256";
|
||||
|
||||
public static KeyPair generateKeyPair() {
|
||||
long ts = System.currentTimeMillis();
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ASYM_KEY_ALGO, "BC");
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
} catch (Throwable e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create key.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Symmetric
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static byte[] encrypt(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
return cipher.doFinal(payload);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] decrypt(byte[] encryptedPayload, SecretKey secretKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
return cipher.doFinal(encryptedPayload);
|
||||
} catch (Throwable e) {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Hmac
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static byte[] getPayloadWithHmac(byte[] payload, SecretKey secretKey) {
|
||||
byte[] payloadWithHmac;
|
||||
try {
|
||||
|
||||
ByteArrayOutputStream outputStream = null;
|
||||
try {
|
||||
byte[] hmac = getHmac(payload, secretKey);
|
||||
outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(payload);
|
||||
outputStream.write(hmac);
|
||||
outputStream.flush();
|
||||
payloadWithHmac = outputStream.toByteArray().clone();
|
||||
} catch (IOException | NoSuchProviderException e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create hmac");
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create hmac");
|
||||
}
|
||||
return payloadWithHmac;
|
||||
}
|
||||
|
||||
|
||||
private static boolean verifyHmac(byte[] message, byte[] hmac, SecretKey secretKey) {
|
||||
try {
|
||||
byte[] hmacTest = getHmac(message, secretKey);
|
||||
return Arrays.equals(hmacTest, hmac);
|
||||
} catch (Throwable e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create cipher");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getHmac(byte[] payload, SecretKey secretKey) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||
Mac mac = Mac.getInstance(HMAC, "BC");
|
||||
mac.init(secretKey);
|
||||
return mac.doFinal(payload);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Symmetric with Hmac
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public static byte[] encryptPayloadWithHmac(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||
return encrypt(getPayloadWithHmac(payload, secretKey), secretKey);
|
||||
}
|
||||
|
||||
public static byte[] decryptPayloadWithHmac(byte[] encryptedPayloadWithHmac, SecretKey secretKey) throws CryptoException {
|
||||
byte[] payloadWithHmac = decrypt(encryptedPayloadWithHmac, secretKey);
|
||||
String payloadWithHmacAsHex = Hex.toHexString(payloadWithHmac);
|
||||
// first part is raw message
|
||||
int length = payloadWithHmacAsHex.length();
|
||||
int sep = length - 64;
|
||||
String payloadAsHex = payloadWithHmacAsHex.substring(0, sep);
|
||||
// last 64 bytes is hmac
|
||||
String hmacAsHex = payloadWithHmacAsHex.substring(sep, length);
|
||||
if (verifyHmac(Hex.decode(payloadAsHex), Hex.decode(hmacAsHex), secretKey)) {
|
||||
return Hex.decode(payloadAsHex);
|
||||
} else {
|
||||
throw new CryptoException("Hmac does not match.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Asymmetric
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static byte[] encryptSecretKey(SecretKey secretKey, PublicKey publicKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
||||
cipher.init(Cipher.WRAP_MODE, publicKey);
|
||||
return cipher.wrap(secretKey);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException("Couldn't encrypt payload");
|
||||
}
|
||||
}
|
||||
|
||||
public static SecretKey decryptSecretKey(byte[] encryptedSecretKey, PrivateKey privateKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
||||
cipher.init(Cipher.UNWRAP_MODE, privateKey);
|
||||
return (SecretKey) cipher.unwrap(encryptedSecretKey, "AES", Cipher.SECRET_KEY);
|
||||
} catch (Throwable e) {
|
||||
// errors when trying to decrypt foreign network_messages are normal
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Hybrid with signature of asymmetric key
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static SecretKey generateSecretKey() {
|
||||
try {
|
||||
KeyGenerator keyPairGenerator = KeyGenerator.getInstance(SYM_KEY_ALGO, "BC");
|
||||
keyPairGenerator.init(256);
|
||||
return keyPairGenerator.generateKey();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new RuntimeException("Couldn't generate key");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getPublicKeyBytes(PublicKey encryptionPubKey) {
|
||||
return new X509EncodedKeySpec(encryptionPubKey.getEncoded()).getEncoded();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encryptionPubKeyBytes
|
||||
* @return
|
||||
*/
|
||||
public static PublicKey getPublicKeyFromBytes(byte[] encryptionPubKeyBytes) {
|
||||
try {
|
||||
return KeyFactory.getInstance(Encryption.ASYM_KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(encryptionPubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
log.error("Error creating sigPublicKey from bytes. sigPublicKeyBytes as hex={}, error={}", Utilities.bytesAsHexString(encryptionPubKeyBytes), e);
|
||||
e.printStackTrace();
|
||||
throw new KeyConversionException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
@Slf4j
|
||||
public class Hash {
|
||||
|
||||
/**
|
||||
* @param data Data as byte array
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getSha256Hash(byte[] data) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256", "BC");
|
||||
digest.update(data, 0, data.length);
|
||||
return digest.digest();
|
||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
log.error("Could not create MessageDigest for hash. " + e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message UTF-8 encoded message
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getSha256Hash(String message) {
|
||||
return getSha256Hash(message.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data data as Integer
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getSha256Hash(Integer data) {
|
||||
return getSha256Hash(ByteBuffer.allocate(4).putInt(data).array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates RIPEMD160(SHA256(input)).
|
||||
*/
|
||||
public static byte[] getSha256Ripemd160hash(byte[] data) {
|
||||
return Utils.sha256hash160(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
public class KeyConversionException extends RuntimeException {
|
||||
public KeyConversionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public KeyConversionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.security.KeyPair;
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@Slf4j
|
||||
public class KeyRing {
|
||||
private final KeyPair signatureKeyPair;
|
||||
private final KeyPair encryptionKeyPair;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
// We generate by default a PGP keypair but the user can set his own if he prefers.
|
||||
// Not impl. yet but prepared in data structure
|
||||
@Nullable
|
||||
@Setter
|
||||
// TODO remove Nullable once impl.
|
||||
private PGPKeyPair pgpKeyPair;
|
||||
|
||||
@Inject
|
||||
public KeyRing(KeyStorage keyStorage) {
|
||||
if (keyStorage.allKeyFilesExist()) {
|
||||
signatureKeyPair = keyStorage.loadKeyPair(KeyStorage.KeyEntry.MSG_SIGNATURE);
|
||||
encryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.KeyEntry.MSG_ENCRYPTION);
|
||||
|
||||
// TODO not impl
|
||||
pgpKeyPair = keyStorage.loadPgpKeyPair(KeyStorage.KeyEntry.PGP);
|
||||
} else {
|
||||
// First time we create key pairs
|
||||
signatureKeyPair = Sig.generateKeyPair();
|
||||
encryptionKeyPair = Encryption.generateKeyPair();
|
||||
|
||||
// TODO not impl
|
||||
pgpKeyPair = PGP.generateKeyPair();
|
||||
keyStorage.saveKeyRing(this);
|
||||
}
|
||||
// TODO remove Nullable once impl.
|
||||
final PGPPublicKey pgpPublicKey = pgpKeyPair != null ? pgpKeyPair.getPublicKey() : null;
|
||||
pubKeyRing = new PubKeyRing(signatureKeyPair.getPublic(), encryptionKeyPair.getPublic(), pgpPublicKey);
|
||||
}
|
||||
|
||||
// Don't print keys for security reasons
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeyRing{" +
|
||||
"signatureKeyPair.hashCode()=" + signatureKeyPair.hashCode() +
|
||||
", encryptionKeyPair.hashCode()=" + encryptionKeyPair.hashCode() +
|
||||
", pubKeyRing.hashCode()=" + pubKeyRing.hashCode() +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bisq.common.storage.FileUtil;
|
||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.DSAParams;
|
||||
import java.security.interfaces.DSAPrivateKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.spec.*;
|
||||
import java.util.Date;
|
||||
|
||||
// TODO: use a password protection for key storage
|
||||
public class KeyStorage {
|
||||
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
||||
|
||||
public static final String KEY_STORAGE_DIR = "keyStorageDir";
|
||||
|
||||
public enum KeyEntry {
|
||||
MSG_SIGNATURE("sig", Sig.KEY_ALGO),
|
||||
MSG_ENCRYPTION("enc", Encryption.ASYM_KEY_ALGO),
|
||||
// TODO not impl
|
||||
PGP("pgp", null);
|
||||
|
||||
private final String fileName;
|
||||
private final String algorithm;
|
||||
|
||||
KeyEntry(String fileName, String algorithm) {
|
||||
this.fileName = fileName;
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Key{" +
|
||||
"fileName='" + fileName + '\'' +
|
||||
", algorithm='" + algorithm + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private final File storageDir;
|
||||
|
||||
@Inject
|
||||
public KeyStorage(@Named(KEY_STORAGE_DIR) File storageDir) {
|
||||
this.storageDir = storageDir;
|
||||
}
|
||||
|
||||
public boolean allKeyFilesExist() {
|
||||
return fileExists(KeyEntry.MSG_SIGNATURE) && fileExists(KeyEntry.MSG_ENCRYPTION);
|
||||
}
|
||||
|
||||
private boolean fileExists(KeyEntry keyEntry) {
|
||||
return new File(storageDir + "/" + keyEntry.getFileName() + ".key").exists();
|
||||
}
|
||||
|
||||
// TODO not impl
|
||||
@SuppressWarnings({"SameParameterValue", "SameReturnValue", "UnusedParameters"})
|
||||
@Nullable
|
||||
public PGPKeyPair loadPgpKeyPair(KeyEntry keyEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public KeyPair loadKeyPair(KeyEntry keyEntry) {
|
||||
FileUtil.rollingBackup(storageDir, keyEntry.getFileName() + ".key", 20);
|
||||
// long now = System.currentTimeMillis();
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm(), "BC");
|
||||
PublicKey publicKey;
|
||||
PrivateKey privateKey;
|
||||
|
||||
File filePrivateKey = new File(storageDir + "/" + keyEntry.getFileName() + ".key");
|
||||
try (FileInputStream fis = new FileInputStream(filePrivateKey.getPath())) {
|
||||
byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()];
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
fis.read(encodedPrivateKey);
|
||||
|
||||
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
|
||||
privateKey = keyFactory.generatePrivate(privateKeySpec);
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
log.error(e.getMessage());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not load key " + keyEntry.toString(), e);
|
||||
}
|
||||
|
||||
if (privateKey instanceof RSAPrivateCrtKey) {
|
||||
RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
|
||||
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPublicExponent());
|
||||
publicKey = keyFactory.generatePublic(publicKeySpec);
|
||||
} else if (privateKey instanceof DSAPrivateKey) {
|
||||
DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privateKey;
|
||||
DSAParams dsaParams = dsaPrivateKey.getParams();
|
||||
BigInteger p = dsaParams.getP();
|
||||
BigInteger q = dsaParams.getQ();
|
||||
BigInteger g = dsaParams.getG();
|
||||
BigInteger y = g.modPow(dsaPrivateKey.getX(), p);
|
||||
KeySpec publicKeySpec = new DSAPublicKeySpec(y, p, q, g);
|
||||
publicKey = keyFactory.generatePublic(publicKeySpec);
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported key algo" + keyEntry.getAlgorithm());
|
||||
}
|
||||
|
||||
log.debug("load completed in {} msec", System.currentTimeMillis() - new Date().getTime());
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new RuntimeException("Could not load key " + keyEntry.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveKeyRing(KeyRing keyRing) {
|
||||
savePrivateKey(keyRing.getSignatureKeyPair().getPrivate(), KeyEntry.MSG_SIGNATURE.getFileName());
|
||||
savePrivateKey(keyRing.getEncryptionKeyPair().getPrivate(), KeyEntry.MSG_ENCRYPTION.getFileName());
|
||||
}
|
||||
|
||||
private void savePrivateKey(PrivateKey privateKey, String name) {
|
||||
if (!storageDir.exists())
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
storageDir.mkdir();
|
||||
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
|
||||
try (FileOutputStream fos = new FileOutputStream(storageDir + "/" + name + ".key")) {
|
||||
fos.write(pkcs8EncodedKeySpec.getEncoded());
|
||||
} catch (IOException e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not save key " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
public class LimitedKeyStrengthException extends Exception {
|
||||
public LimitedKeyStrengthException() {
|
||||
super("Default crypto policy has not been changed. Only weak keys with length 128 are allowed by the default policy.");
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.bcpg.BCPGKey;
|
||||
import org.bouncycastle.bcpg.RSAPublicBCPGKey;
|
||||
import org.bouncycastle.openpgp.*;
|
||||
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Iterator;
|
||||
|
||||
@SuppressWarnings("UnusedAssignment")
|
||||
@Slf4j
|
||||
public class PGP {
|
||||
|
||||
// TODO not tested yet, remove Nullable once impl.
|
||||
// PEM encoding
|
||||
@Nullable
|
||||
public static PGPPublicKey getPubKeyFromPem(@Nullable String pem) {
|
||||
if (pem != null) {
|
||||
InputStream inputStream = new ByteArrayInputStream(pem.getBytes());
|
||||
try {
|
||||
inputStream = PGPUtil.getDecoderStream(inputStream);
|
||||
try {
|
||||
JcaPGPPublicKeyRingCollection ringCollection = new JcaPGPPublicKeyRingCollection(inputStream);
|
||||
Iterator<PGPPublicKeyRing> keyRingsIterator = ringCollection.getKeyRings();
|
||||
while (keyRingsIterator.hasNext()) {
|
||||
PGPPublicKeyRing pgpPublicKeyRing = keyRingsIterator.next();
|
||||
Iterator<PGPPublicKey> pubKeysIterator = pgpPublicKeyRing.getPublicKeys();
|
||||
while (pubKeysIterator.hasNext()) {
|
||||
final PGPPublicKey pgpPublicKey = pubKeysIterator.next();
|
||||
if ((pgpPublicKey).isEncryptionKey()) {
|
||||
log.debug(pgpPublicKey.getClass().getName()
|
||||
+ " KeyID: " + Long.toHexString(pgpPublicKey.getKeyID())
|
||||
+ " type: " + pgpPublicKey.getAlgorithm()
|
||||
+ " fingerprint: " + new String(Hex.encode(pgpPublicKey.getFingerprint())));
|
||||
|
||||
BCPGKey bcKey = pgpPublicKey.getPublicKeyPacket().getKey();
|
||||
log.debug(bcKey.getClass().getName());
|
||||
if (bcKey instanceof RSAPublicBCPGKey) {
|
||||
RSAPublicBCPGKey bcRSA = (RSAPublicBCPGKey) bcKey;
|
||||
RSAPublicKeySpec specRSA = new RSAPublicKeySpec(bcRSA.getModulus(), bcRSA.getPublicExponent());
|
||||
PublicKey jceKey = KeyFactory.getInstance("RSA").generatePublic(specRSA);
|
||||
// if you want to use the key in JCE, use jceKey
|
||||
// if you want to write "X.509" (SPKI) DER format to a file:
|
||||
//Files.write(new File(pubKeyAsString).toPath(), jceKey.getEncoded());
|
||||
// if you want to write in PEM, bouncycastle can do that
|
||||
// or you can just do base64 and add BEGIN/END lines
|
||||
// return pubKeyAsString; // assume only one key; if need to handle multiple keys
|
||||
// or select other than the first, specify more clearly
|
||||
}
|
||||
|
||||
return pgpPublicKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (PGPException | InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
log.error("Error creating publicKey from pem. pem={}, error={}", pem, e);
|
||||
e.printStackTrace();
|
||||
throw new KeyConversionException(e);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating publicKey from pem. pem={}, error={}", pem, e);
|
||||
e.printStackTrace();
|
||||
throw new KeyConversionException(e);
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("Error creating publicKey from pem. pem=null");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO not impl, remove Nullable once impl.
|
||||
// PEM encoding
|
||||
@SuppressWarnings({"SameReturnValue", "UnusedParameters"})
|
||||
@NotNull
|
||||
public static String getPEMFromPubKey(@Nullable PGPPublicKey pgpPubKey) {
|
||||
// We use empty string as we must not have null in proto file
|
||||
return "";
|
||||
}
|
||||
|
||||
// TODO not impl, remove Nullable once impl.
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
@Nullable
|
||||
public static PGPKeyPair generateKeyPair() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.bisq.common.consensus.UsedForTradeContractJson;
|
||||
import io.bisq.common.proto.network.NetworkPayload;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* Same as KeyRing but with public keys only.
|
||||
* Used to send public keys over the wire to other peer.
|
||||
*/
|
||||
@Slf4j
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
public final class PubKeyRing implements NetworkPayload, UsedForTradeContractJson {
|
||||
private final byte[] signaturePubKeyBytes;
|
||||
private final byte[] encryptionPubKeyBytes;
|
||||
@Nullable
|
||||
private final String pgpPubKeyAsPem;
|
||||
|
||||
private transient PublicKey signaturePubKey;
|
||||
private transient PublicKey encryptionPubKey;
|
||||
@Nullable
|
||||
private transient PGPPublicKey pgpPubKey;
|
||||
|
||||
public PubKeyRing(PublicKey signaturePubKey, PublicKey encryptionPubKey, @Nullable PGPPublicKey pgpPubKey) {
|
||||
this.signaturePubKeyBytes = Sig.getPublicKeyBytes(signaturePubKey);
|
||||
this.encryptionPubKeyBytes = Encryption.getPublicKeyBytes(encryptionPubKey);
|
||||
this.pgpPubKeyAsPem = PGP.getPEMFromPubKey(pgpPubKey);
|
||||
|
||||
this.signaturePubKey = signaturePubKey;
|
||||
this.encryptionPubKey = encryptionPubKey;
|
||||
this.pgpPubKey = pgpPubKey;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@VisibleForTesting
|
||||
public PubKeyRing(byte[] signaturePubKeyBytes, byte[] encryptionPubKeyBytes, @Nullable String pgpPubKeyAsPem) {
|
||||
this.signaturePubKeyBytes = signaturePubKeyBytes;
|
||||
this.encryptionPubKeyBytes = encryptionPubKeyBytes;
|
||||
this.pgpPubKeyAsPem = pgpPubKeyAsPem;
|
||||
|
||||
signaturePubKey = Sig.getPublicKeyFromBytes(signaturePubKeyBytes);
|
||||
encryptionPubKey = Encryption.getPublicKeyFromBytes(encryptionPubKeyBytes);
|
||||
if (pgpPubKeyAsPem != null)
|
||||
pgpPubKey = PGP.getPubKeyFromPem(pgpPubKeyAsPem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PB.PubKeyRing toProtoMessage() {
|
||||
return PB.PubKeyRing.newBuilder()
|
||||
.setSignaturePubKeyBytes(ByteString.copyFrom(signaturePubKeyBytes))
|
||||
.setEncryptionPubKeyBytes(ByteString.copyFrom(encryptionPubKeyBytes))
|
||||
.setPgpPubKeyAsPem(pgpPubKeyAsPem)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static PubKeyRing fromProto(PB.PubKeyRing proto) {
|
||||
return new PubKeyRing(proto.getSignaturePubKeyBytes().toByteArray(),
|
||||
proto.getEncryptionPubKeyBytes().toByteArray(),
|
||||
proto.getPgpPubKeyAsPem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PubKeyRing{" +
|
||||
"signaturePubKeyHex=" + Utilities.bytesAsHexString(signaturePubKeyBytes) +
|
||||
", encryptionPubKeyHex=" + Utilities.bytesAsHexString(encryptionPubKeyBytes) +
|
||||
", pgpPubKeyAsString=" + pgpPubKeyAsPem +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.bisq.common.proto.network.NetworkPayload;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.Value;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
@Value
|
||||
public final class SealedAndSigned implements NetworkPayload {
|
||||
private final byte[] encryptedSecretKey;
|
||||
private final byte[] encryptedPayloadWithHmac;
|
||||
private final byte[] signature;
|
||||
private final byte[] sigPublicKeyBytes;
|
||||
transient private final PublicKey sigPublicKey;
|
||||
|
||||
public SealedAndSigned(byte[] encryptedSecretKey,
|
||||
byte[] encryptedPayloadWithHmac,
|
||||
byte[] signature,
|
||||
PublicKey sigPublicKey) {
|
||||
this.encryptedSecretKey = encryptedSecretKey;
|
||||
this.encryptedPayloadWithHmac = encryptedPayloadWithHmac;
|
||||
this.signature = signature;
|
||||
this.sigPublicKey = sigPublicKey;
|
||||
|
||||
sigPublicKeyBytes = Sig.getPublicKeyBytes(sigPublicKey);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private SealedAndSigned(byte[] encryptedSecretKey,
|
||||
byte[] encryptedPayloadWithHmac,
|
||||
byte[] signature,
|
||||
byte[] sigPublicKeyBytes) {
|
||||
this.encryptedSecretKey = encryptedSecretKey;
|
||||
this.encryptedPayloadWithHmac = encryptedPayloadWithHmac;
|
||||
this.signature = signature;
|
||||
this.sigPublicKeyBytes = sigPublicKeyBytes;
|
||||
|
||||
sigPublicKey = Sig.getPublicKeyFromBytes(sigPublicKeyBytes);
|
||||
}
|
||||
|
||||
public PB.SealedAndSigned toProtoMessage() {
|
||||
return PB.SealedAndSigned.newBuilder()
|
||||
.setEncryptedSecretKey(ByteString.copyFrom(encryptedSecretKey))
|
||||
.setEncryptedPayloadWithHmac(ByteString.copyFrom(encryptedPayloadWithHmac))
|
||||
.setSignature(ByteString.copyFrom(signature))
|
||||
.setSigPublicKeyBytes(ByteString.copyFrom(sigPublicKeyBytes))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SealedAndSigned fromProto(PB.SealedAndSigned proto) {
|
||||
return new SealedAndSigned(proto.getEncryptedSecretKey().toByteArray(),
|
||||
proto.getEncryptedPayloadWithHmac().toByteArray(),
|
||||
proto.getSignature().toByteArray(),
|
||||
proto.getSigPublicKeyBytes().toByteArray());
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.crypto;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
/**
|
||||
* StorageSignatureKeyPair/STORAGE_SIGN_KEY_ALGO: That is used for signing the data to be stored to the P2P network (by flooding).
|
||||
* The algo is selected because it originated from the TomP2P version which used DSA.
|
||||
* Changing to EC keys might be considered.
|
||||
* <p/>
|
||||
* MsgSignatureKeyPair/MSG_SIGN_KEY_ALGO/MSG_SIGN_ALGO: That is used when sending a message to a peer which is encrypted and signed.
|
||||
* Changing to EC keys might be considered.
|
||||
*/
|
||||
public class Sig {
|
||||
private static final Logger log = LoggerFactory.getLogger(Sig.class);
|
||||
|
||||
public static final String KEY_ALGO = "DSA";
|
||||
private static final String ALGO = "SHA256withDSA";
|
||||
|
||||
|
||||
/**
|
||||
* @return keyPair
|
||||
*/
|
||||
public static KeyPair generateKeyPair() {
|
||||
long ts = System.currentTimeMillis();
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGO, "BC");
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
throw new RuntimeException("Could not create key.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param privateKey
|
||||
* @param data
|
||||
* @return
|
||||
* @throws SignatureException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static byte[] sign(PrivateKey privateKey, byte[] data) throws CryptoException {
|
||||
try {
|
||||
Signature sig = Signature.getInstance(ALGO, "BC");
|
||||
sig.initSign(privateKey);
|
||||
sig.update(data);
|
||||
return sig.sign();
|
||||
} catch (SignatureException | NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new CryptoException("Signing failed. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param privateKey
|
||||
* @param message UTF-8 encoded message to sign
|
||||
* @return Base64 encoded signature
|
||||
* @throws SignatureException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static String sign(PrivateKey privateKey, String message) throws CryptoException {
|
||||
byte[] sigAsBytes = sign(privateKey, message.getBytes(Charsets.UTF_8));
|
||||
return Base64.toBase64String(sigAsBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publicKey
|
||||
* @param data
|
||||
* @param signature
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws CryptoException {
|
||||
try {
|
||||
Signature sig = Signature.getInstance(ALGO, "BC");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data);
|
||||
return sig.verify(signature);
|
||||
} catch (SignatureException | NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new CryptoException("Signature verification failed. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publicKey
|
||||
* @param message UTF-8 encoded message
|
||||
* @param signature Base64 encoded signature
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
public static boolean verify(PublicKey publicKey, String message, String signature) throws CryptoException {
|
||||
return verify(publicKey, message.getBytes(Charsets.UTF_8), Base64.decode(signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sigPublicKeyBytes
|
||||
* @return
|
||||
*/
|
||||
public static PublicKey getPublicKeyFromBytes(byte[] sigPublicKeyBytes) {
|
||||
try {
|
||||
return KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(sigPublicKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
log.error("Error creating sigPublicKey from bytes. sigPublicKeyBytes as hex={}, error={}", Utilities.bytesAsHexString(sigPublicKeyBytes), e);
|
||||
e.printStackTrace();
|
||||
throw new KeyConversionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getPublicKeyBytes(PublicKey sigPublicKey) {
|
||||
return new X509EncodedKeySpec(sigPublicKey.getEncoded()).getEncoded();
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.handlers;
|
||||
|
||||
/**
|
||||
* For reporting error message only (UI)
|
||||
*/
|
||||
public interface ErrorMessageHandler {
|
||||
void handleErrorMessage(String errorMessage);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.handlers;
|
||||
|
||||
/**
|
||||
* For reporting throwable objects only
|
||||
*/
|
||||
public interface ExceptionHandler {
|
||||
void handleException(Throwable throwable);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.handlers;
|
||||
|
||||
/**
|
||||
* For reporting a description message and throwable
|
||||
*/
|
||||
public interface FaultHandler {
|
||||
void handleFault(String errorMessage, Throwable throwable);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.handlers;
|
||||
|
||||
public interface ResultHandler {
|
||||
void handleResult();
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class BankUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BankUtil.class);
|
||||
|
||||
// BankName
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public static boolean isBankNameRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
case "US":
|
||||
case "NZ":
|
||||
case "AU":
|
||||
case "CA":
|
||||
case "SE":
|
||||
case "HK":
|
||||
// We show always the bank name as it is needed in specific banks.
|
||||
// Though that handling should be optimized in futures.
|
||||
return true;
|
||||
// return false;
|
||||
case "MX":
|
||||
case "BR":
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBankNameLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "BR":
|
||||
return Res.get("payment.bank.name");
|
||||
default:
|
||||
return isBankNameRequired(countryCode) ? Res.get("payment.bank.name") : Res.get("payment.bank.nameOptional");
|
||||
}
|
||||
}
|
||||
|
||||
// BankId
|
||||
public static boolean isBankIdRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
case "US":
|
||||
case "BR":
|
||||
case "NZ":
|
||||
case "AU":
|
||||
case "SE":
|
||||
case "CL":
|
||||
case "NO":
|
||||
return false;
|
||||
case "CA":
|
||||
case "MX":
|
||||
case "HK":
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBankIdLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "CA":
|
||||
return "Institution Number:";// do not translate as it is used in english only
|
||||
case "MX":
|
||||
case "HK":
|
||||
return Res.get("payment.bankCode");
|
||||
default:
|
||||
return isBankIdRequired(countryCode) ? Res.get("payment.bankId") : Res.get("payment.bankIdOptional");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// BranchId
|
||||
public static boolean isBranchIdRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
case "US":
|
||||
case "BR":
|
||||
case "AU":
|
||||
case "CA":
|
||||
return true;
|
||||
case "NZ":
|
||||
case "MX":
|
||||
case "HK":
|
||||
case "SE":
|
||||
case "NO":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBranchIdLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
return "UK sort code:"; // do not translate as it is used in english only
|
||||
case "US":
|
||||
return "Routing Number:"; // do not translate as it is used in english only
|
||||
case "BR":
|
||||
return "Código da Agência:"; // do not translate as it is used in portuguese only
|
||||
case "AU":
|
||||
return "BSB code:"; // do not translate as it is used in english only
|
||||
case "CA":
|
||||
return "Transit Number:"; // do not translate as it is used in english only
|
||||
default:
|
||||
return isBranchIdRequired(countryCode) ? Res.get("payment.branchNr") : Res.get("payment.branchNrOptional");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AccountNr
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public static boolean isAccountNrRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccountNrLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
case "US":
|
||||
case "BR":
|
||||
case "NZ":
|
||||
case "AU":
|
||||
case "CA":
|
||||
case "HK":
|
||||
return Res.get("payment.accountNr");
|
||||
case "NO":
|
||||
return "Kontonummer:"; // do not translate as it is used in norwegian only
|
||||
case "SE":
|
||||
return "Bankgiro number:"; // do not translate as it is used in swedish only
|
||||
case "MX":
|
||||
return "CLABE:"; // do not translate as it is used in spanish only
|
||||
case "CL":
|
||||
return "Cuenta:"; // do not translate as it is used in spanish only
|
||||
default:
|
||||
return Res.get("payment.accountNrLabel");
|
||||
}
|
||||
}
|
||||
|
||||
// AccountType
|
||||
public static boolean isAccountTypeRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "US":
|
||||
case "BR":
|
||||
case "CA":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccountTypeLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "US":
|
||||
case "BR":
|
||||
case "CA":
|
||||
return Res.get("payment.accountType");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getAccountTypeValues(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "US":
|
||||
case "BR":
|
||||
case "CA":
|
||||
return Arrays.asList(Res.get("payment.checking"), Res.get("payment.savings"));
|
||||
default:
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HolderId
|
||||
public static boolean isHolderIdRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "BR":
|
||||
case "CL":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHolderIdLabel(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "BR":
|
||||
return "Cadastro de Pessoas Físicas (CPF):"; // do not translate as it is used in portuguese only
|
||||
case "CL":
|
||||
return "Rol Único Tributario (RUT):"; // do not translate as it is used in spanish only
|
||||
default:
|
||||
return Res.get("payment.personalId");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHolderIdLabelShort(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "BR":
|
||||
return "CPF:"; // do not translate as it is used in portuguese only
|
||||
case "CL":
|
||||
return "RUT:"; // do not translate as it is used in spanish only
|
||||
default:
|
||||
return "ID";
|
||||
}
|
||||
}
|
||||
|
||||
// Validation
|
||||
public static boolean useValidation(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "GB":
|
||||
case "US":
|
||||
case "BR":
|
||||
case "AU":
|
||||
case "CA":
|
||||
case "NZ":
|
||||
case "MX":
|
||||
case "HK":
|
||||
case "SE":
|
||||
case "NO":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isStateRequired(String countryCode) {
|
||||
switch (countryCode) {
|
||||
case "US":
|
||||
case "CA":
|
||||
case "AU":
|
||||
case "MY":
|
||||
case "MX":
|
||||
case "CN":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.proto.persistable.PersistablePayload;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public final class Country implements PersistablePayload {
|
||||
public final String code;
|
||||
public final String name;
|
||||
public final Region region;
|
||||
|
||||
public Country(String code, String name, Region region) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return PB.Country.newBuilder().setCode(code).setName(name)
|
||||
.setRegion(PB.Region.newBuilder().setCode(region.code).setName(region.name)).build();
|
||||
}
|
||||
|
||||
public static Country fromProto(PB.Country proto) {
|
||||
return new Country(proto.getCode(),
|
||||
proto.getName(),
|
||||
Region.fromProto(proto.getRegion()));
|
||||
}
|
||||
}
|
@ -1,476 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import io.bisq.common.GlobalSettings;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class CountryUtil {
|
||||
public static List<Country> getAllSepaEuroCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"AT", "BE", "CY", "DE", "EE", "FI", "FR", "GR", "IE",
|
||||
"IT", "LV", "LT", "LU", "MC", "MT", "NL", "PT", "SK", "SI", "ES"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaInstantEuroCountries() {
|
||||
return getAllSepaEuroCountries();
|
||||
}
|
||||
|
||||
private static void populateCountryListByCodes(List<Country> list, String[] codes) {
|
||||
for (String code : codes) {
|
||||
Locale locale = new Locale(LanguageUtil.getDefaultLanguage(), code, "");
|
||||
final String countryCode = locale.getCountry();
|
||||
String regionCode = getRegionCode(countryCode);
|
||||
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||
Country country = new Country(countryCode, locale.getDisplayCountry(), region);
|
||||
if (countryCode.equals("XK"))
|
||||
country = new Country(countryCode, getNameByCode(countryCode), region);
|
||||
list.add(country);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean containsAllSepaEuroCountries(List<String> countryCodesToCompare) {
|
||||
countryCodesToCompare.sort(String::compareTo);
|
||||
List<String> countryCodesBase = getAllSepaEuroCountries().stream().map(c -> c.code).collect(Collectors.toList());
|
||||
return countryCodesToCompare.toString().equals(countryCodesBase.toString());
|
||||
}
|
||||
|
||||
public static boolean containsAllSepaInstantEuroCountries(List<String> countryCodesToCompare) {
|
||||
return containsAllSepaEuroCountries(countryCodesToCompare);
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaNonEuroCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"BG", "HR", "CZ", "DK", "GB", "HU", "PL", "RO",
|
||||
"SE", "IS", "NO", "LI", "CH"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaInstantNonEuroCountries() {
|
||||
return getAllSepaNonEuroCountries();
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
list.addAll(getAllSepaEuroCountries());
|
||||
list.addAll(getAllSepaNonEuroCountries());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaInstantCountries() {
|
||||
// TODO find reliable source for list
|
||||
// //Austria, Estonia, Germany, Italy, Latvia, Lithuania, the Netherlands and Spain.
|
||||
|
||||
/* List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"AT", "DE", "EE",
|
||||
"IT", "LV", "LT", "NL", "ES"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));*/
|
||||
return getAllSepaCountries();
|
||||
}
|
||||
|
||||
public static Country getDefaultCountry() {
|
||||
String regionCode = getRegionCode(getLocale().getCountry());
|
||||
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||
return new Country(getLocale().getCountry(), getLocale().getDisplayCountry(), region);
|
||||
}
|
||||
|
||||
public static Optional<Country> findCountryByCode(String countryCode) {
|
||||
return getAllCountries().stream().filter(e -> e.code.equals(countryCode)).findAny();
|
||||
}
|
||||
|
||||
public static String getNameByCode(String countryCode) {
|
||||
if (countryCode.equals("XK"))
|
||||
return "Republic of Kosovo";
|
||||
else
|
||||
return new Locale(LanguageUtil.getDefaultLanguage(), countryCode).getDisplayCountry();
|
||||
}
|
||||
|
||||
public static String getNameAndCode(String countryCode) {
|
||||
return getNameByCode(countryCode) + " (" + countryCode + ")";
|
||||
}
|
||||
|
||||
public static String getCodesString(List<String> countryCodes) {
|
||||
return countryCodes.stream().collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
public static String getNamesByCodesString(List<String> countryCodes) {
|
||||
return getNamesByCodes(countryCodes).stream().collect(Collectors.joining(",\n"));
|
||||
}
|
||||
|
||||
public static List<Region> getAllRegions() {
|
||||
final List<Region> allRegions = new ArrayList<>();
|
||||
|
||||
String regionCode = "AM";
|
||||
Region region = new Region(regionCode, getRegionName(regionCode));
|
||||
allRegions.add(region);
|
||||
|
||||
regionCode = "AF";
|
||||
region = new Region(regionCode, getRegionName(regionCode));
|
||||
allRegions.add(region);
|
||||
|
||||
regionCode = "EU";
|
||||
region = new Region(regionCode, getRegionName(regionCode));
|
||||
allRegions.add(region);
|
||||
|
||||
regionCode = "AS";
|
||||
region = new Region(regionCode, getRegionName(regionCode));
|
||||
allRegions.add(region);
|
||||
|
||||
regionCode = "OC";
|
||||
region = new Region(regionCode, getRegionName(regionCode));
|
||||
allRegions.add(region);
|
||||
|
||||
return allRegions;
|
||||
}
|
||||
|
||||
public static List<Country> getAllCountriesForRegion(Region selectedRegion) {
|
||||
return Lists.newArrayList(Collections2.filter(getAllCountries(), country ->
|
||||
selectedRegion != null && country != null && selectedRegion.equals(country.region)));
|
||||
}
|
||||
|
||||
public static List<Country> getAllCountries() {
|
||||
final Set<Country> allCountries = new HashSet<>();
|
||||
for (final Locale locale : getAllCountryLocales()) {
|
||||
String regionCode = getRegionCode(locale.getCountry());
|
||||
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||
Country country = new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
||||
if (locale.getCountry().equals("XK"))
|
||||
country = new Country(locale.getCountry(), "Republic of Kosovo", region);
|
||||
allCountries.add(country);
|
||||
}
|
||||
|
||||
allCountries.add(new Country("GE", "Georgia", new Region("AS", getRegionName("AS"))));
|
||||
allCountries.add(new Country("BW", "Botswana", new Region("AF", getRegionName("AF"))));
|
||||
allCountries.add(new Country("IR", "Iran", new Region("AS", getRegionName("AS"))));
|
||||
|
||||
final List<Country> allCountriesList = new ArrayList<>(allCountries);
|
||||
allCountriesList.sort((locale1, locale2) -> locale1.name.compareTo(locale2.name));
|
||||
return allCountriesList;
|
||||
}
|
||||
|
||||
private static List<Locale> getAllCountryLocales() {
|
||||
List<Locale> allLocales = LocaleUtil.getAllLocales();
|
||||
|
||||
// Filter duplicate locale entries
|
||||
Set<Locale> allLocalesAsSet = allLocales.stream().filter(locale -> !locale.getCountry().isEmpty())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Locale> allCountryLocales = new ArrayList<>();
|
||||
allCountryLocales.addAll(allLocalesAsSet);
|
||||
allCountryLocales.sort((locale1, locale2) -> locale1.getDisplayCountry().compareTo(locale2.getDisplayCountry()));
|
||||
return allCountryLocales;
|
||||
}
|
||||
|
||||
private static List<String> getNamesByCodes(List<String> countryCodes) {
|
||||
return countryCodes.stream().map(CountryUtil::getNameByCode).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String getRegionName(final String regionCode) {
|
||||
return regionCodeToNameMap.get(regionCode);
|
||||
}
|
||||
|
||||
private static final Map<String, String> regionCodeToNameMap = new HashMap<>();
|
||||
// Key is: ISO 3166 code, value is region code as defined in regionCodeToNameMap
|
||||
private static final Map<String, String> regionByCountryCodeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
regionCodeToNameMap.put("AM", "Americas");
|
||||
regionCodeToNameMap.put("AF", "Africa");
|
||||
regionCodeToNameMap.put("EU", "Europe");
|
||||
regionCodeToNameMap.put("AS", "Asia");
|
||||
regionCodeToNameMap.put("OC", "Oceania");
|
||||
|
||||
// Data extracted from https://restcountries.eu/rest/v2/all?fields=name;region;subregion;alpha2Code;languages
|
||||
regionByCountryCodeMap.put("AF", "AS"); // name=Afghanistan / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("AX", "EU"); // name=Åland Islands / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("AL", "EU"); // name=Albania / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("DZ", "AF"); // name=Algeria / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("AS", "OC"); // name=American Samoa / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("AD", "EU"); // name=Andorra / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("AO", "AF"); // name=Angola / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("AI", "AM"); // name=Anguilla / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("AG", "AM"); // name=Antigua and Barbuda / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("AR", "AM"); // name=Argentina / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("AM", "AS"); // name=Armenia / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("AW", "AM"); // name=Aruba / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("AU", "OC"); // name=Australia / region=Oceania / subregion=Australia and New Zealand
|
||||
regionByCountryCodeMap.put("AT", "EU"); // name=Austria / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("AZ", "AS"); // name=Azerbaijan / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("BS", "AM"); // name=Bahamas / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("BH", "AS"); // name=Bahrain / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("BD", "AS"); // name=Bangladesh / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("BB", "AM"); // name=Barbados / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("BY", "EU"); // name=Belarus / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("BE", "EU"); // name=Belgium / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("BZ", "AM"); // name=Belize / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("BJ", "AF"); // name=Benin / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("BM", "AM"); // name=Bermuda / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("BT", "AS"); // name=Bhutan / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("BO", "AM"); // name=Bolivia (Plurinational State of) / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("BQ", "AM"); // name=Bonaire, Sint Eustatius and Saba / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("BA", "EU"); // name=Bosnia and Herzegovina / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("BW", "AF"); // name=Botswana / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("BR", "AM"); // name=Brazil / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("IO", "AF"); // name=British Indian Ocean Territory / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("UM", "AM"); // name=United States Minor Outlying Islands / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("VG", "AM"); // name=Virgin Islands (British) / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("VI", "AM"); // name=Virgin Islands (U.S.) / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("BN", "AS"); // name=Brunei Darussalam / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("BG", "EU"); // name=Bulgaria / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("BF", "AF"); // name=Burkina Faso / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("BI", "AF"); // name=Burundi / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("KH", "AS"); // name=Cambodia / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("CM", "AF"); // name=Cameroon / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("CA", "AM"); // name=Canada / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("CV", "AF"); // name=Cabo Verde / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("KY", "AM"); // name=Cayman Islands / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("CF", "AF"); // name=Central African Republic / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("TD", "AF"); // name=Chad / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("CL", "AM"); // name=Chile / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("CN", "AS"); // name=China / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("CX", "OC"); // name=Christmas Island / region=Oceania / subregion=Australia and New Zealand
|
||||
regionByCountryCodeMap.put("CC", "OC"); // name=Cocos (Keeling) Islands / region=Oceania / subregion=Australia and New Zealand
|
||||
regionByCountryCodeMap.put("CO", "AM"); // name=Colombia / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("KM", "AF"); // name=Comoros / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("CG", "AF"); // name=Congo / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("CD", "AF"); // name=Congo (Democratic Republic of the) / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("CK", "OC"); // name=Cook Islands / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("CR", "AM"); // name=Costa Rica / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("HR", "EU"); // name=Croatia / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("CU", "AM"); // name=Cuba / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("CW", "AM"); // name=Curaçao / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("CY", "EU"); // name=Cyprus / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("CZ", "EU"); // name=Czech Republic / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("DK", "EU"); // name=Denmark / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("DJ", "AF"); // name=Djibouti / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("DM", "AM"); // name=Dominica / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("DO", "AM"); // name=Dominican Republic / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("EC", "AM"); // name=Ecuador / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("EG", "AF"); // name=Egypt / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("SV", "AM"); // name=El Salvador / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("GQ", "AF"); // name=Equatorial Guinea / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("ER", "AF"); // name=Eritrea / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("EE", "EU"); // name=Estonia / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("ET", "AF"); // name=Ethiopia / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("FK", "AM"); // name=Falkland Islands (Malvinas) / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("FO", "EU"); // name=Faroe Islands / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("FJ", "OC"); // name=Fiji / region=Oceania / subregion=Melanesia
|
||||
regionByCountryCodeMap.put("FI", "EU"); // name=Finland / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("FR", "EU"); // name=France / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("GF", "AM"); // name=French Guiana / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("PF", "OC"); // name=French Polynesia / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("TF", "AF"); // name=French Southern Territories / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("GA", "AF"); // name=Gabon / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("GM", "AF"); // name=Gambia / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("GE", "AS"); // name=Georgia / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("DE", "EU"); // name=Germany / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("GH", "AF"); // name=Ghana / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("GI", "EU"); // name=Gibraltar / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("GR", "EU"); // name=Greece / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("GL", "AM"); // name=Greenland / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("GD", "AM"); // name=Grenada / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("GP", "AM"); // name=Guadeloupe / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("GU", "OC"); // name=Guam / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("GT", "AM"); // name=Guatemala / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("GG", "EU"); // name=Guernsey / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("GN", "AF"); // name=Guinea / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("GW", "AF"); // name=Guinea-Bissau / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("GY", "AM"); // name=Guyana / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("HT", "AM"); // name=Haiti / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("VA", "EU"); // name=Holy See / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("HN", "AM"); // name=Honduras / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("HK", "AS"); // name=Hong Kong / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("HU", "EU"); // name=Hungary / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("IS", "EU"); // name=Iceland / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("IN", "AS"); // name=India / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("ID", "AS"); // name=Indonesia / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("CI", "AF"); // name=Côte d'Ivoire / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("IR", "AS"); // name=Iran (Islamic Republic of) / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("IQ", "AS"); // name=Iraq / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("IE", "EU"); // name=Ireland / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("IM", "EU"); // name=Isle of Man / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("IL", "AS"); // name=Israel / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("IT", "EU"); // name=Italy / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("JM", "AM"); // name=Jamaica / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("JP", "AS"); // name=Japan / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("JE", "EU"); // name=Jersey / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("JO", "AS"); // name=Jordan / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("KZ", "AS"); // name=Kazakhstan / region=Asia / subregion=Central Asia
|
||||
regionByCountryCodeMap.put("KE", "AF"); // name=Kenya / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("KI", "OC"); // name=Kiribati / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("KW", "AS"); // name=Kuwait / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("KG", "AS"); // name=Kyrgyzstan / region=Asia / subregion=Central Asia
|
||||
regionByCountryCodeMap.put("LA", "AS"); // name=Lao People's Democratic Republic / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("LV", "EU"); // name=Latvia / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("LB", "AS"); // name=Lebanon / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("LS", "AF"); // name=Lesotho / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("LR", "AF"); // name=Liberia / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("LY", "AF"); // name=Libya / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("LI", "EU"); // name=Liechtenstein / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("LT", "EU"); // name=Lithuania / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("LU", "EU"); // name=Luxembourg / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("MO", "AS"); // name=Macao / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("MK", "EU"); // name=Macedonia (the former Yugoslav Republic of) / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("MG", "AF"); // name=Madagascar / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("MW", "AF"); // name=Malawi / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("MY", "AS"); // name=Malaysia / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("MV", "AS"); // name=Maldives / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("ML", "AF"); // name=Mali / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("MT", "EU"); // name=Malta / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("MH", "OC"); // name=Marshall Islands / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("MQ", "AM"); // name=Martinique / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("MR", "AF"); // name=Mauritania / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("MU", "AF"); // name=Mauritius / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("YT", "AF"); // name=Mayotte / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("MX", "AM"); // name=Mexico / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("FM", "OC"); // name=Micronesia (Federated States of) / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("MD", "EU"); // name=Moldova (Republic of) / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("MC", "EU"); // name=Monaco / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("MN", "AS"); // name=Mongolia / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("ME", "EU"); // name=Montenegro / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("MS", "AM"); // name=Montserrat / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("MA", "AF"); // name=Morocco / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("MZ", "AF"); // name=Mozambique / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("MM", "AS"); // name=Myanmar / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("NA", "AF"); // name=Namibia / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("NR", "OC"); // name=Nauru / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("NP", "AS"); // name=Nepal / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("NL", "EU"); // name=Netherlands / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("NC", "OC"); // name=New Caledonia / region=Oceania / subregion=Melanesia
|
||||
regionByCountryCodeMap.put("NZ", "OC"); // name=New Zealand / region=Oceania / subregion=Australia and New Zealand
|
||||
regionByCountryCodeMap.put("NI", "AM"); // name=Nicaragua / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("NE", "AF"); // name=Niger / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("NG", "AF"); // name=Nigeria / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("NU", "OC"); // name=Niue / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("NF", "OC"); // name=Norfolk Island / region=Oceania / subregion=Australia and New Zealand
|
||||
regionByCountryCodeMap.put("KP", "AS"); // name=Korea (Democratic People's Republic of) / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("MP", "OC"); // name=Northern Mariana Islands / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("NO", "EU"); // name=Norway / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("OM", "AS"); // name=Oman / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("PK", "AS"); // name=Pakistan / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("PW", "OC"); // name=Palau / region=Oceania / subregion=Micronesia
|
||||
regionByCountryCodeMap.put("PS", "AS"); // name=Palestine, State of / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("PA", "AM"); // name=Panama / region=Americas / subregion=Central America
|
||||
regionByCountryCodeMap.put("PG", "OC"); // name=Papua New Guinea / region=Oceania / subregion=Melanesia
|
||||
regionByCountryCodeMap.put("PY", "AM"); // name=Paraguay / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("PE", "AM"); // name=Peru / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("PH", "AS"); // name=Philippines / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("PN", "OC"); // name=Pitcairn / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("PL", "EU"); // name=Poland / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("PT", "EU"); // name=Portugal / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("PR", "AM"); // name=Puerto Rico / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("QA", "AS"); // name=Qatar / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("XK", "EU"); // name=Republic of Kosovo / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("RE", "AF"); // name=Réunion / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("RO", "EU"); // name=Romania / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("RU", "EU"); // name=Russian Federation / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("RW", "AF"); // name=Rwanda / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("BL", "AM"); // name=Saint Barthélemy / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("SH", "AF"); // name=Saint Helena, Ascension and Tristan da Cunha / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("KN", "AM"); // name=Saint Kitts and Nevis / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("LC", "AM"); // name=Saint Lucia / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("MF", "AM"); // name=Saint Martin (French part) / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("PM", "AM"); // name=Saint Pierre and Miquelon / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("VC", "AM"); // name=Saint Vincent and the Grenadines / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("WS", "OC"); // name=Samoa / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("SM", "EU"); // name=San Marino / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("ST", "AF"); // name=Sao Tome and Principe / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("SA", "AS"); // name=Saudi Arabia / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("SN", "AF"); // name=Senegal / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("RS", "EU"); // name=Serbia / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("SC", "AF"); // name=Seychelles / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("SL", "AF"); // name=Sierra Leone / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("SG", "AS"); // name=Singapore / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("SX", "AM"); // name=Sint Maarten (Dutch part) / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("SK", "EU"); // name=Slovakia / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("SI", "EU"); // name=Slovenia / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("SB", "OC"); // name=Solomon Islands / region=Oceania / subregion=Melanesia
|
||||
regionByCountryCodeMap.put("SO", "AF"); // name=Somalia / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("ZA", "AF"); // name=South Africa / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("GS", "AM"); // name=South Georgia and the South Sandwich Islands / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("KR", "AS"); // name=Korea (Republic of) / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("SS", "AF"); // name=South Sudan / region=Africa / subregion=Middle Africa
|
||||
regionByCountryCodeMap.put("ES", "EU"); // name=Spain / region=Europe / subregion=Southern Europe
|
||||
regionByCountryCodeMap.put("LK", "AS"); // name=Sri Lanka / region=Asia / subregion=Southern Asia
|
||||
regionByCountryCodeMap.put("SD", "AF"); // name=Sudan / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("SR", "AM"); // name=Suriname / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("SJ", "EU"); // name=Svalbard and Jan Mayen / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("SZ", "AF"); // name=Swaziland / region=Africa / subregion=Southern Africa
|
||||
regionByCountryCodeMap.put("SE", "EU"); // name=Sweden / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("CH", "EU"); // name=Switzerland / region=Europe / subregion=Western Europe
|
||||
regionByCountryCodeMap.put("SY", "AS"); // name=Syrian Arab Republic / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("TW", "AS"); // name=Taiwan / region=Asia / subregion=Eastern Asia
|
||||
regionByCountryCodeMap.put("TJ", "AS"); // name=Tajikistan / region=Asia / subregion=Central Asia
|
||||
regionByCountryCodeMap.put("TZ", "AF"); // name=Tanzania, United Republic of / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("TH", "AS"); // name=Thailand / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("TL", "AS"); // name=Timor-Leste / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("TG", "AF"); // name=Togo / region=Africa / subregion=Western Africa
|
||||
regionByCountryCodeMap.put("TK", "OC"); // name=Tokelau / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("TO", "OC"); // name=Tonga / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("TT", "AM"); // name=Trinidad and Tobago / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("TN", "AF"); // name=Tunisia / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("TR", "AS"); // name=Turkey / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("TM", "AS"); // name=Turkmenistan / region=Asia / subregion=Central Asia
|
||||
regionByCountryCodeMap.put("TC", "AM"); // name=Turks and Caicos Islands / region=Americas / subregion=Caribbean
|
||||
regionByCountryCodeMap.put("TV", "OC"); // name=Tuvalu / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("UG", "AF"); // name=Uganda / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("UA", "EU"); // name=Ukraine / region=Europe / subregion=Eastern Europe
|
||||
regionByCountryCodeMap.put("AE", "AS"); // name=United Arab Emirates / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("GB", "EU"); // name=United Kingdom of Great Britain and Northern Ireland / region=Europe / subregion=Northern Europe
|
||||
regionByCountryCodeMap.put("US", "AM"); // name=United States of America / region=Americas / subregion=Northern America
|
||||
regionByCountryCodeMap.put("UY", "AM"); // name=Uruguay / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("UZ", "AS"); // name=Uzbekistan / region=Asia / subregion=Central Asia
|
||||
regionByCountryCodeMap.put("VU", "OC"); // name=Vanuatu / region=Oceania / subregion=Melanesia
|
||||
regionByCountryCodeMap.put("VE", "AM"); // name=Venezuela (Bolivarian Republic of) / region=Americas / subregion=South America
|
||||
regionByCountryCodeMap.put("VN", "AS"); // name=Viet Nam / region=Asia / subregion=South-Eastern Asia
|
||||
regionByCountryCodeMap.put("WF", "OC"); // name=Wallis and Futuna / region=Oceania / subregion=Polynesia
|
||||
regionByCountryCodeMap.put("EH", "AF"); // name=Western Sahara / region=Africa / subregion=Northern Africa
|
||||
regionByCountryCodeMap.put("YE", "AS"); // name=Yemen / region=Asia / subregion=Western Asia
|
||||
regionByCountryCodeMap.put("ZM", "AF"); // name=Zambia / region=Africa / subregion=Eastern Africa
|
||||
regionByCountryCodeMap.put("ZW", "AF"); // name=Zimbabwe / region=Africa / subregion=Eastern Africa
|
||||
}
|
||||
|
||||
public static String getRegionCode(String countryCode) {
|
||||
if (regionByCountryCodeMap.containsKey(countryCode))
|
||||
return regionByCountryCodeMap.get(countryCode);
|
||||
else
|
||||
return "Undefined";
|
||||
}
|
||||
|
||||
public static String getDefaultCountryCode() {
|
||||
// might be set later in pref or config, so not use Preferences.getDefaultLocale() anywhere in the code
|
||||
return getLocale().getCountry();
|
||||
}
|
||||
|
||||
private static Locale getLocale() {
|
||||
return GlobalSettings.getLocale();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public final class CryptoCurrency extends TradeCurrency {
|
||||
// http://boschista.deviantart.com/journal/Cool-ASCII-Symbols-214218618
|
||||
private final static String PREFIX = "✦ ";
|
||||
|
||||
@Getter
|
||||
private boolean isAsset = false;
|
||||
|
||||
public CryptoCurrency(String currencyCode,
|
||||
String name) {
|
||||
this(currencyCode, name, false);
|
||||
}
|
||||
|
||||
public CryptoCurrency(String currencyCode,
|
||||
String name,
|
||||
boolean isAsset) {
|
||||
super(currencyCode, name);
|
||||
this.isAsset = isAsset;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return getTradeCurrencyBuilder()
|
||||
.setCryptoCurrency(PB.CryptoCurrency.newBuilder()
|
||||
.setIsAsset(isAsset))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static CryptoCurrency fromProto(PB.TradeCurrency proto) {
|
||||
return new CryptoCurrency(proto.getCode(),
|
||||
proto.getName(),
|
||||
proto.getCryptoCurrency().getIsAsset());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public String getDisplayPrefix() {
|
||||
return PREFIX;
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class CurrencyTuple {
|
||||
public final String code;
|
||||
public final String name;
|
||||
public final int precision; // precision 4 is 1/10000 -> 0.0001 is smallest unit
|
||||
|
||||
public CurrencyTuple(String code, String name) {
|
||||
// We use Fiat class and there precision is 4
|
||||
// In future we might add custom precision per currency
|
||||
this(code, name, 4);
|
||||
}
|
||||
|
||||
public CurrencyTuple(String code, String name, int precision) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.precision = precision;
|
||||
}
|
||||
}
|
@ -1,390 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import io.bisq.common.GlobalSettings;
|
||||
import io.bisq.common.app.DevEnv;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class CurrencyUtil {
|
||||
private static String baseCurrencyCode = "BTC";
|
||||
|
||||
public static void setBaseCurrencyCode(String baseCurrencyCode) {
|
||||
CurrencyUtil.baseCurrencyCode = baseCurrencyCode;
|
||||
}
|
||||
|
||||
private static List<FiatCurrency> allSortedFiatCurrencies;
|
||||
|
||||
private static List<FiatCurrency> createAllSortedFiatCurrenciesList() {
|
||||
Set<FiatCurrency> set = CountryUtil.getAllCountries().stream()
|
||||
.map(country -> getCurrencyByCountryCode(country.code))
|
||||
.collect(Collectors.toSet());
|
||||
List<FiatCurrency> list = new ArrayList<>(set);
|
||||
list.sort(TradeCurrency::compareTo);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<FiatCurrency> getAllSortedFiatCurrencies() {
|
||||
if (Objects.isNull(allSortedFiatCurrencies)) {
|
||||
allSortedFiatCurrencies = createAllSortedFiatCurrenciesList();
|
||||
}
|
||||
return allSortedFiatCurrencies;
|
||||
}
|
||||
|
||||
|
||||
public static List<FiatCurrency> getMainFiatCurrencies() {
|
||||
TradeCurrency defaultTradeCurrency = getDefaultTradeCurrency();
|
||||
List<FiatCurrency> list = new ArrayList<>();
|
||||
// Top traded currencies
|
||||
list.add(new FiatCurrency("USD"));
|
||||
list.add(new FiatCurrency("EUR"));
|
||||
list.add(new FiatCurrency("GBP"));
|
||||
list.add(new FiatCurrency("CAD"));
|
||||
list.add(new FiatCurrency("AUD"));
|
||||
list.add(new FiatCurrency("RUB"));
|
||||
list.add(new FiatCurrency("INR"));
|
||||
|
||||
list.sort(TradeCurrency::compareTo);
|
||||
|
||||
FiatCurrency defaultFiatCurrency = defaultTradeCurrency instanceof FiatCurrency ? (FiatCurrency) defaultTradeCurrency : null;
|
||||
if (defaultFiatCurrency != null && list.contains(defaultFiatCurrency)) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
list.remove(defaultTradeCurrency);
|
||||
list.add(0, defaultFiatCurrency);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<CryptoCurrency> allSortedCryptoCurrencies;
|
||||
|
||||
public static List<CryptoCurrency> getAllSortedCryptoCurrencies() {
|
||||
if (allSortedCryptoCurrencies == null)
|
||||
allSortedCryptoCurrencies = createAllSortedCryptoCurrenciesList();
|
||||
return allSortedCryptoCurrencies;
|
||||
}
|
||||
|
||||
// Don't make a PR for adding a coin but follow the steps described here:
|
||||
// https://forum.bisq.network/t/how-to-add-your-favorite-altcoin/
|
||||
public static List<CryptoCurrency> createAllSortedCryptoCurrenciesList() {
|
||||
final List<CryptoCurrency> result = new ArrayList<>();
|
||||
|
||||
result.add(new CryptoCurrency("BETR", "Better Betting", true));
|
||||
if (DevEnv.DAO_TRADING_ACTIVATED)
|
||||
result.add(new CryptoCurrency("BSQ", "Bisq Token"));
|
||||
|
||||
if (!baseCurrencyCode.equals("BTC"))
|
||||
result.add(new CryptoCurrency("BTC", "Bitcoin"));
|
||||
result.add(new CryptoCurrency("BCH", "Bitcoin Cash"));
|
||||
result.add(new CryptoCurrency("BCHC", "Bitcoin Clashic"));
|
||||
result.add(new CryptoCurrency("BTG", "Bitcoin Gold"));
|
||||
result.add(new CryptoCurrency("BURST", "Burstcoin"));
|
||||
result.add(new CryptoCurrency("GBYTE", "Byte"));
|
||||
result.add(new CryptoCurrency("CAGE", "Cagecoin"));
|
||||
result.add(new CryptoCurrency("XCP", "Counterparty"));
|
||||
result.add(new CryptoCurrency("CREA", "Creativecoin"));
|
||||
result.add(new CryptoCurrency("XCN", "Cryptonite"));
|
||||
result.add(new CryptoCurrency("DNET", "DarkNet"));
|
||||
if (!baseCurrencyCode.equals("DASH"))
|
||||
result.add(new CryptoCurrency("DASH", "Dash"));
|
||||
result.add(new CryptoCurrency("DCT", "DECENT"));
|
||||
result.add(new CryptoCurrency("DCR", "Decred"));
|
||||
result.add(new CryptoCurrency("ONION", "DeepOnion"));
|
||||
result.add(new CryptoCurrency("DOGE", "Dogecoin"));
|
||||
result.add(new CryptoCurrency("DMC", "DynamicCoin"));
|
||||
result.add(new CryptoCurrency("ELLA", "Ellaism"));
|
||||
result.add(new CryptoCurrency("ESP", "Espers"));
|
||||
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||
result.add(new CryptoCurrency("ETC", "Ether Classic"));
|
||||
result.add(new CryptoCurrency("XIN", "Infinity Economics"));
|
||||
result.add(new CryptoCurrency("IOP", "Internet Of People"));
|
||||
result.add(new CryptoCurrency("INXT", "Internext", true));
|
||||
result.add(new CryptoCurrency("GRC", "Gridcoin"));
|
||||
result.add(new CryptoCurrency("LBC", "LBRY Credits"));
|
||||
result.add(new CryptoCurrency("LSK", "Lisk"));
|
||||
if (!baseCurrencyCode.equals("LTC"))
|
||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||
result.add(new CryptoCurrency("MAID", "MaidSafeCoin"));
|
||||
result.add(new CryptoCurrency("MDC", "Madcoin"));
|
||||
result.add(new CryptoCurrency("XMR", "Monero"));
|
||||
result.add(new CryptoCurrency("MT", "Mycelium Token", true));
|
||||
result.add(new CryptoCurrency("NAV", "Nav Coin"));
|
||||
result.add(new CryptoCurrency("NMC", "Namecoin"));
|
||||
result.add(new CryptoCurrency("NBT", "NuBits"));
|
||||
result.add(new CryptoCurrency("NXT", "Nxt"));
|
||||
result.add(new CryptoCurrency("888", "OctoCoin"));
|
||||
result.add(new CryptoCurrency("PART", "Particl"));
|
||||
result.add(new CryptoCurrency("PASC", "Pascal Coin", true));
|
||||
result.add(new CryptoCurrency("PEPECASH", "Pepe Cash"));
|
||||
result.add(new CryptoCurrency("PIVX", "PIVX"));
|
||||
result.add(new CryptoCurrency("POST", "PostCoin"));
|
||||
result.add(new CryptoCurrency("PNC", "Pranacoin"));
|
||||
result.add(new CryptoCurrency("RDD", "ReddCoin"));
|
||||
result.add(new CryptoCurrency("REF", "RefToken", true));
|
||||
result.add(new CryptoCurrency("SFSC", "Safe FileSystem Coin"));
|
||||
result.add(new CryptoCurrency("SC", "Siacoin"));
|
||||
result.add(new CryptoCurrency("SF", "Siafund"));
|
||||
result.add(new CryptoCurrency("SIB", "Sibcoin"));
|
||||
result.add(new CryptoCurrency("XSPEC", "Spectrecoin"));
|
||||
result.add(new CryptoCurrency("STEEM", "STEEM"));
|
||||
|
||||
result.add(new CryptoCurrency("TRC", "Terracoin"));
|
||||
result.add(new CryptoCurrency("MVT", "The Movement", true));
|
||||
|
||||
result.add(new CryptoCurrency("UNO", "Unobtanium"));
|
||||
result.add(new CryptoCurrency("CRED", "Verify", true));
|
||||
result.add(new CryptoCurrency("WAC", "WACoins"));
|
||||
result.add(new CryptoCurrency("WILD", "WILD Token", true));
|
||||
result.add(new CryptoCurrency("XZC", "Zcoin"));
|
||||
result.add(new CryptoCurrency("ZEC", "Zcash"));
|
||||
result.add(new CryptoCurrency("ZEN", "ZenCash"));
|
||||
|
||||
// Added 0.6.6
|
||||
result.add(new CryptoCurrency("STL", "Stellite"));
|
||||
result.add(new CryptoCurrency("DAI", "Dai Stablecoin", true));
|
||||
result.add(new CryptoCurrency("YTN", "Yenten"));
|
||||
result.add(new CryptoCurrency("DARX", "BitDaric"));
|
||||
result.add(new CryptoCurrency("ODN", "Obsidian"));
|
||||
result.add(new CryptoCurrency("CDT", "Cassubian Detk"));
|
||||
result.add(new CryptoCurrency("DGM", "DigiMoney"));
|
||||
result.add(new CryptoCurrency("SCS", "SpeedCash"));
|
||||
result.add(new CryptoCurrency("SOS", "SOS Coin", true));
|
||||
result.add(new CryptoCurrency("ACH", "AchieveCoin"));
|
||||
result.add(new CryptoCurrency("VDN", "vDinar"));
|
||||
result.add(new CryptoCurrency("WMCC", "WorldMobileCoin"));
|
||||
|
||||
// Added 0.7.0
|
||||
result.add(new CryptoCurrency("ALC", "Angelcoin"));
|
||||
result.add(new CryptoCurrency("DIN", "Dinero"));
|
||||
result.add(new CryptoCurrency("NAH", "Strayacoin"));
|
||||
result.add(new CryptoCurrency("ROI", "ROIcoin"));
|
||||
|
||||
result.sort(TradeCurrency::compareTo);
|
||||
|
||||
// Util for printing all altcoins for adding to FAQ page
|
||||
|
||||
/* StringBuilder sb = new StringBuilder();
|
||||
result.stream().forEach(e -> sb.append("<li>“")
|
||||
.append(e.getCode())
|
||||
.append("”, “")
|
||||
.append(e.getName())
|
||||
.append("”</li>")
|
||||
.append("\n"));
|
||||
log.info(sb.toString());*/
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<CryptoCurrency> getMainCryptoCurrencies() {
|
||||
final List<CryptoCurrency> result = new ArrayList<>();
|
||||
if (DevEnv.DAO_TRADING_ACTIVATED)
|
||||
result.add(new CryptoCurrency("BSQ", "Bisq Token"));
|
||||
if (!baseCurrencyCode.equals("BTC"))
|
||||
result.add(new CryptoCurrency("BTC", "Bitcoin"));
|
||||
if (!baseCurrencyCode.equals("DASH"))
|
||||
result.add(new CryptoCurrency("DASH", "Dash"));
|
||||
result.add(new CryptoCurrency("DCR", "Decred"));
|
||||
result.add(new CryptoCurrency("ONION", "DeepOnion"));
|
||||
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||
result.add(new CryptoCurrency("ETC", "Ether Classic"));
|
||||
result.add(new CryptoCurrency("GRC", "Gridcoin"));
|
||||
if (!baseCurrencyCode.equals("LTC"))
|
||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||
result.add(new CryptoCurrency("XMR", "Monero"));
|
||||
result.add(new CryptoCurrency("MT", "Mycelium Token", true));
|
||||
result.add(new CryptoCurrency("NMC", "Namecoin"));
|
||||
result.add(new CryptoCurrency("SC", "Siacoin"));
|
||||
result.add(new CryptoCurrency("SF", "Siafund"));
|
||||
result.add(new CryptoCurrency("UNO", "Unobtanium"));
|
||||
result.add(new CryptoCurrency("ZEC", "Zcash"));
|
||||
result.sort(TradeCurrency::compareTo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Sorted list of SEPA currencies with EUR as first item
|
||||
*/
|
||||
private static Set<TradeCurrency> getSortedSEPACurrencyCodes() {
|
||||
return CountryUtil.getAllSepaCountries().stream()
|
||||
.map(country -> getCurrencyByCountryCode(country.code))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
// At OKPay you can exchange internally those currencies
|
||||
public static List<TradeCurrency> getAllOKPayCurrencies() {
|
||||
ArrayList<TradeCurrency> currencies = new ArrayList<>(Arrays.asList(
|
||||
new FiatCurrency("EUR"),
|
||||
new FiatCurrency("USD"),
|
||||
new FiatCurrency("GBP"),
|
||||
new FiatCurrency("CHF"),
|
||||
new FiatCurrency("RUB"),
|
||||
new FiatCurrency("PLN"),
|
||||
new FiatCurrency("JPY"),
|
||||
new FiatCurrency("CAD"),
|
||||
new FiatCurrency("AUD"),
|
||||
new FiatCurrency("CZK"),
|
||||
new FiatCurrency("NOK"),
|
||||
new FiatCurrency("SEK"),
|
||||
new FiatCurrency("DKK"),
|
||||
new FiatCurrency("HRK"),
|
||||
new FiatCurrency("HUF"),
|
||||
new FiatCurrency("NZD"),
|
||||
new FiatCurrency("RON"),
|
||||
new FiatCurrency("TRY"),
|
||||
new FiatCurrency("ZAR"),
|
||||
new FiatCurrency("HKD"),
|
||||
new FiatCurrency("CNY")
|
||||
));
|
||||
currencies.sort(Comparator.comparing(TradeCurrency::getCode));
|
||||
return currencies;
|
||||
}
|
||||
|
||||
// https://support.uphold.com/hc/en-us/articles/202473803-Supported-currencies
|
||||
public static List<TradeCurrency> getAllUpholdCurrencies() {
|
||||
ArrayList<TradeCurrency> currencies = new ArrayList<>(Arrays.asList(
|
||||
new FiatCurrency("USD"),
|
||||
new FiatCurrency("EUR"),
|
||||
new FiatCurrency("GBP"),
|
||||
new FiatCurrency("CNY"),
|
||||
new FiatCurrency("JPY"),
|
||||
new FiatCurrency("CHF"),
|
||||
new FiatCurrency("INR"),
|
||||
new FiatCurrency("MXN"),
|
||||
new FiatCurrency("AUD"),
|
||||
new FiatCurrency("CAD"),
|
||||
new FiatCurrency("HKD"),
|
||||
new FiatCurrency("NZD"),
|
||||
new FiatCurrency("SGD"),
|
||||
new FiatCurrency("KES"),
|
||||
new FiatCurrency("ILS"),
|
||||
new FiatCurrency("DKK"),
|
||||
new FiatCurrency("NOK"),
|
||||
new FiatCurrency("SEK"),
|
||||
new FiatCurrency("PLN"),
|
||||
new FiatCurrency("ARS"),
|
||||
new FiatCurrency("BRL"),
|
||||
new FiatCurrency("AED"),
|
||||
new FiatCurrency("PHP")
|
||||
));
|
||||
|
||||
currencies.sort(Comparator.comparing(TradeCurrency::getCode));
|
||||
return currencies;
|
||||
}
|
||||
|
||||
//https://www.revolut.com/pa/faq#can-i-hold-multiple-currencies
|
||||
public static List<TradeCurrency> getAllRevolutCurrencies() {
|
||||
ArrayList<TradeCurrency> currencies = new ArrayList<>(Arrays.asList(
|
||||
new FiatCurrency("USD"),
|
||||
new FiatCurrency("GBP"),
|
||||
new FiatCurrency("EUR"),
|
||||
new FiatCurrency("PLN"),
|
||||
new FiatCurrency("CHF"),
|
||||
new FiatCurrency("DKK"),
|
||||
new FiatCurrency("NOK"),
|
||||
new FiatCurrency("SEK"),
|
||||
new FiatCurrency("RON"),
|
||||
new FiatCurrency("SGD"),
|
||||
new FiatCurrency("HKD"),
|
||||
new FiatCurrency("AUD"),
|
||||
new FiatCurrency("NZD"),
|
||||
new FiatCurrency("TRY"),
|
||||
new FiatCurrency("ILS"),
|
||||
new FiatCurrency("AED"),
|
||||
new FiatCurrency("CAD"),
|
||||
new FiatCurrency("HUF"),
|
||||
new FiatCurrency("INR"),
|
||||
new FiatCurrency("JPY"),
|
||||
new FiatCurrency("MAD"),
|
||||
new FiatCurrency("QAR"),
|
||||
new FiatCurrency("THB"),
|
||||
new FiatCurrency("ZAR")
|
||||
));
|
||||
|
||||
currencies.sort(Comparator.comparing(TradeCurrency::getCode));
|
||||
return currencies;
|
||||
}
|
||||
|
||||
public static boolean isFiatCurrency(String currencyCode) {
|
||||
try {
|
||||
return currencyCode != null && !currencyCode.isEmpty() && !isCryptoCurrency(currencyCode) && Currency.getInstance(currencyCode) != null;
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<FiatCurrency> getFiatCurrency(String currencyCode) {
|
||||
return getAllSortedFiatCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny();
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static boolean isCryptoCurrency(String currencyCode) {
|
||||
return getCryptoCurrency(currencyCode).isPresent();
|
||||
}
|
||||
|
||||
public static Optional<CryptoCurrency> getCryptoCurrency(String currencyCode) {
|
||||
return getAllSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny();
|
||||
}
|
||||
|
||||
public static Optional<TradeCurrency> getTradeCurrency(String currencyCode) {
|
||||
Optional<FiatCurrency> fiatCurrencyOptional = getFiatCurrency(currencyCode);
|
||||
if (isFiatCurrency(currencyCode) && fiatCurrencyOptional.isPresent()) {
|
||||
return Optional.of(fiatCurrencyOptional.get());
|
||||
} else {
|
||||
Optional<CryptoCurrency> cryptoCurrencyOptional = getCryptoCurrency(currencyCode);
|
||||
if (isCryptoCurrency(currencyCode) && cryptoCurrencyOptional.isPresent()) {
|
||||
return Optional.of(cryptoCurrencyOptional.get());
|
||||
} else {
|
||||
return Optional.<TradeCurrency>empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static FiatCurrency getCurrencyByCountryCode(String countryCode) {
|
||||
if (countryCode.equals("XK"))
|
||||
return new FiatCurrency("EUR");
|
||||
else
|
||||
return new FiatCurrency(Currency.getInstance(new Locale(LanguageUtil.getDefaultLanguage(), countryCode)).getCurrencyCode());
|
||||
}
|
||||
|
||||
|
||||
public static String getNameByCode(String currencyCode) {
|
||||
if (isCryptoCurrency(currencyCode))
|
||||
return getCryptoCurrency(currencyCode).get().getName();
|
||||
else
|
||||
try {
|
||||
return Currency.getInstance(currencyCode).getDisplayName();
|
||||
} catch (Throwable t) {
|
||||
log.debug("No currency name available " + t.getMessage());
|
||||
return currencyCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getNameAndCode(String currencyCode) {
|
||||
return getNameByCode(currencyCode) + " (" + currencyCode + ")";
|
||||
}
|
||||
|
||||
public static TradeCurrency getDefaultTradeCurrency() {
|
||||
return GlobalSettings.getDefaultTradeCurrency();
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.GlobalSettings;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Currency;
|
||||
import java.util.Locale;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString
|
||||
@Getter
|
||||
public final class FiatCurrency extends TradeCurrency {
|
||||
// http://boschista.deviantart.com/journal/Cool-ASCII-Symbols-214218618
|
||||
private final static String PREFIX = "★ ";
|
||||
|
||||
private final Currency currency;
|
||||
|
||||
public FiatCurrency(String currencyCode) {
|
||||
this(Currency.getInstance(currencyCode), getLocale());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public FiatCurrency(Currency currency) {
|
||||
this(currency, getLocale());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public FiatCurrency(Currency currency, Locale locale) {
|
||||
super(currency.getCurrencyCode(), currency.getDisplayName(locale));
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
PB.Currency.Builder currencyBuilder = PB.Currency.newBuilder().setCurrencyCode(currency.getCurrencyCode());
|
||||
PB.FiatCurrency.Builder fiatCurrencyBuilder = PB.FiatCurrency.newBuilder().setCurrency(currencyBuilder);
|
||||
return getTradeCurrencyBuilder()
|
||||
.setFiatCurrency(fiatCurrencyBuilder)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static FiatCurrency fromProto(PB.TradeCurrency proto) {
|
||||
return new FiatCurrency(proto.getCode());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static Locale getLocale() {
|
||||
return GlobalSettings.getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayPrefix() {
|
||||
return PREFIX;
|
||||
}
|
||||
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import io.bisq.common.GlobalSettings;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class LanguageUtil {
|
||||
private static final List<String> userLanguageCodes = Arrays.asList(
|
||||
"en", // English
|
||||
"de", // German
|
||||
"el", // Greek
|
||||
"es", // Spanish
|
||||
"pt", // Portuguese / Brazil
|
||||
"sr", // Serbian
|
||||
"zh", // Chinese
|
||||
"hu", // Hungarian
|
||||
"ro", // Romanian
|
||||
"ru", // Russian
|
||||
"fr", // French
|
||||
"tr" // Turkish
|
||||
|
||||
/*
|
||||
// not translated yet
|
||||
"it", // Italian
|
||||
"ja", // Japanese
|
||||
"iw", // Hebrew
|
||||
"hi", // Hindi
|
||||
"ko", // Korean
|
||||
"pl", // Polish
|
||||
"sv", // Swedish
|
||||
"no", // Norwegian
|
||||
"nl", // Dutch
|
||||
"be", // Belarusian
|
||||
"fi", // Finnish
|
||||
"bg", // Bulgarian
|
||||
"lt", // Lithuanian
|
||||
"lv", // Latvian
|
||||
"hr", // Croatian
|
||||
"uk", // Ukrainian
|
||||
"sk", // Slovak
|
||||
"sl", // Slovenian
|
||||
"ga", // Irish
|
||||
"sq", // Albanian
|
||||
"ca", // Catalan
|
||||
"mk", // Macedonian
|
||||
"kk", // Kazakh
|
||||
"km", // Khmer
|
||||
"sw", // Swahili
|
||||
"in", // Indonesian
|
||||
"ms", // Malay
|
||||
"is", // Icelandic
|
||||
"et", // Estonian
|
||||
"cs", // Czech
|
||||
"ar", // Arabic
|
||||
"vi", // Vietnamese
|
||||
"th", // Thai
|
||||
"da", // Danish
|
||||
"mt" // Maltese
|
||||
*/
|
||||
);
|
||||
|
||||
public static List<String> getAllLanguageCodes() {
|
||||
List<Locale> allLocales = LocaleUtil.getAllLocales();
|
||||
|
||||
// Filter duplicate locale entries
|
||||
Set<String> allLocalesAsSet = allLocales.stream().filter(locale -> !locale.getLanguage().isEmpty() &&
|
||||
!locale.getDisplayLanguage().isEmpty())
|
||||
.map(Locale::getLanguage)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<String> allLanguageCodes = new ArrayList<>();
|
||||
allLanguageCodes.addAll(allLocalesAsSet);
|
||||
allLanguageCodes.sort((o1, o2) -> getDisplayName(o1).compareTo(getDisplayName(o2)));
|
||||
return allLanguageCodes;
|
||||
}
|
||||
|
||||
public static String getDefaultLanguage() {
|
||||
// might be set later in pref or config, so not use defaultLocale anywhere in the code
|
||||
return getLocale().getLanguage();
|
||||
}
|
||||
|
||||
public static String getDefaultLanguageLocaleAsCode() {
|
||||
return new Locale(LanguageUtil.getDefaultLanguage(), "").getLanguage();
|
||||
}
|
||||
|
||||
public static String getEnglishLanguageLocaleCode() {
|
||||
return new Locale(Locale.ENGLISH.getLanguage(), "").getLanguage();
|
||||
}
|
||||
|
||||
public static String getDisplayName(String code) {
|
||||
Locale locale = new Locale(code.toUpperCase());
|
||||
if (locale.getLanguage().equals("sr")) {
|
||||
// Serbia
|
||||
// shows it in russian by default
|
||||
return "Srpski";
|
||||
} else {
|
||||
return locale.getDisplayName(locale);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getUserLanguageCodes() {
|
||||
return userLanguageCodes;
|
||||
}
|
||||
|
||||
private static Locale getLocale() {
|
||||
return GlobalSettings.getLocale();
|
||||
}
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(LocaleUtil.class);
|
||||
|
||||
public static List<Locale> getAllLocales() {
|
||||
|
||||
// Data from https://restcountries.eu/rest/v2/all?fields=name;region;subregion;alpha2Code;languages
|
||||
List<Locale> allLocales = new ArrayList<>();
|
||||
|
||||
allLocales.add(new Locale("ps", "AF")); // Afghanistan / lang=Pashto
|
||||
allLocales.add(new Locale("sv", "AX")); // Åland Islands / lang=Swedish
|
||||
allLocales.add(new Locale("sq", "AL")); // Albania / lang=Albanian
|
||||
allLocales.add(new Locale("ar", "DZ")); // Algeria / lang=Arabic
|
||||
allLocales.add(new Locale("en", "AS")); // American Samoa / lang=English
|
||||
allLocales.add(new Locale("ca", "AD")); // Andorra / lang=Catalan
|
||||
allLocales.add(new Locale("pt", "AO")); // Angola / lang=Portuguese
|
||||
allLocales.add(new Locale("en", "AI")); // Anguilla / lang=English
|
||||
allLocales.add(new Locale("en", "AG")); // Antigua and Barbuda / lang=English
|
||||
allLocales.add(new Locale("es", "AR")); // Argentina / lang=Spanish
|
||||
allLocales.add(new Locale("hy", "AM")); // Armenia / lang=Armenian
|
||||
allLocales.add(new Locale("nl", "AW")); // Aruba / lang=Dutch
|
||||
allLocales.add(new Locale("en", "AU")); // Australia / lang=English
|
||||
allLocales.add(new Locale("de", "AT")); // Austria / lang=German
|
||||
allLocales.add(new Locale("az", "AZ")); // Azerbaijan / lang=Azerbaijani
|
||||
allLocales.add(new Locale("en", "BS")); // Bahamas / lang=English
|
||||
allLocales.add(new Locale("ar", "BH")); // Bahrain / lang=Arabic
|
||||
allLocales.add(new Locale("bn", "BD")); // Bangladesh / lang=Bengali
|
||||
allLocales.add(new Locale("en", "BB")); // Barbados / lang=English
|
||||
allLocales.add(new Locale("be", "BY")); // Belarus / lang=Belarusian
|
||||
allLocales.add(new Locale("nl", "BE")); // Belgium / lang=Dutch
|
||||
allLocales.add(new Locale("en", "BZ")); // Belize / lang=English
|
||||
allLocales.add(new Locale("fr", "BJ")); // Benin / lang=French
|
||||
allLocales.add(new Locale("en", "BM")); // Bermuda / lang=English
|
||||
allLocales.add(new Locale("dz", "BT")); // Bhutan / lang=Dzongkha
|
||||
allLocales.add(new Locale("es", "BO")); // Bolivia (Plurinational State of) / lang=Spanish
|
||||
allLocales.add(new Locale("nl", "BQ")); // Bonaire, Sint Eustatius and Saba / lang=Dutch
|
||||
allLocales.add(new Locale("bs", "BA")); // Bosnia and Herzegovina / lang=Bosnian
|
||||
allLocales.add(new Locale("en", "BW")); // Botswana / lang=English
|
||||
allLocales.add(new Locale("pt", "BR")); // Brazil / lang=Portuguese
|
||||
allLocales.add(new Locale("en", "IO")); // British Indian Ocean Territory / lang=English
|
||||
allLocales.add(new Locale("en", "UM")); // United States Minor Outlying Islands / lang=English
|
||||
allLocales.add(new Locale("en", "VG")); // Virgin Islands (British) / lang=English
|
||||
allLocales.add(new Locale("en", "VI")); // Virgin Islands (U.S.) / lang=English
|
||||
allLocales.add(new Locale("ms", "BN")); // Brunei Darussalam / lang=Malay
|
||||
allLocales.add(new Locale("bg", "BG")); // Bulgaria / lang=Bulgarian
|
||||
allLocales.add(new Locale("fr", "BF")); // Burkina Faso / lang=French
|
||||
allLocales.add(new Locale("fr", "BI")); // Burundi / lang=French
|
||||
allLocales.add(new Locale("km", "KH")); // Cambodia / lang=Khmer
|
||||
allLocales.add(new Locale("en", "CM")); // Cameroon / lang=English
|
||||
allLocales.add(new Locale("en", "CA")); // Canada / lang=English
|
||||
allLocales.add(new Locale("pt", "CV")); // Cabo Verde / lang=Portuguese
|
||||
allLocales.add(new Locale("en", "KY")); // Cayman Islands / lang=English
|
||||
allLocales.add(new Locale("fr", "CF")); // Central African Republic / lang=French
|
||||
allLocales.add(new Locale("fr", "TD")); // Chad / lang=French
|
||||
allLocales.add(new Locale("es", "CL")); // Chile / lang=Spanish
|
||||
allLocales.add(new Locale("zh", "CN")); // China / lang=Chinese
|
||||
allLocales.add(new Locale("en", "CX")); // Christmas Island / lang=English
|
||||
allLocales.add(new Locale("en", "CC")); // Cocos (Keeling) Islands / lang=English
|
||||
allLocales.add(new Locale("es", "CO")); // Colombia / lang=Spanish
|
||||
allLocales.add(new Locale("ar", "KM")); // Comoros / lang=Arabic
|
||||
allLocales.add(new Locale("fr", "CG")); // Congo / lang=French
|
||||
allLocales.add(new Locale("fr", "CD")); // Congo (Democratic Republic of the) / lang=French
|
||||
allLocales.add(new Locale("en", "CK")); // Cook Islands / lang=English
|
||||
allLocales.add(new Locale("es", "CR")); // Costa Rica / lang=Spanish
|
||||
allLocales.add(new Locale("hr", "HR")); // Croatia / lang=Croatian
|
||||
allLocales.add(new Locale("es", "CU")); // Cuba / lang=Spanish
|
||||
allLocales.add(new Locale("nl", "CW")); // Curaçao / lang=Dutch
|
||||
allLocales.add(new Locale("el", "CY")); // Cyprus / lang=Greek (modern)
|
||||
allLocales.add(new Locale("cs", "CZ")); // Czech Republic / lang=Czech
|
||||
allLocales.add(new Locale("da", "DK")); // Denmark / lang=Danish
|
||||
allLocales.add(new Locale("fr", "DJ")); // Djibouti / lang=French
|
||||
allLocales.add(new Locale("en", "DM")); // Dominica / lang=English
|
||||
allLocales.add(new Locale("es", "DO")); // Dominican Republic / lang=Spanish
|
||||
allLocales.add(new Locale("es", "EC")); // Ecuador / lang=Spanish
|
||||
allLocales.add(new Locale("ar", "EG")); // Egypt / lang=Arabic
|
||||
allLocales.add(new Locale("es", "SV")); // El Salvador / lang=Spanish
|
||||
allLocales.add(new Locale("es", "GQ")); // Equatorial Guinea / lang=Spanish
|
||||
allLocales.add(new Locale("ti", "ER")); // Eritrea / lang=Tigrinya
|
||||
allLocales.add(new Locale("et", "EE")); // Estonia / lang=Estonian
|
||||
allLocales.add(new Locale("am", "ET")); // Ethiopia / lang=Amharic
|
||||
allLocales.add(new Locale("en", "FK")); // Falkland Islands (Malvinas) / lang=English
|
||||
allLocales.add(new Locale("fo", "FO")); // Faroe Islands / lang=Faroese
|
||||
allLocales.add(new Locale("en", "FJ")); // Fiji / lang=English
|
||||
allLocales.add(new Locale("fi", "FI")); // Finland / lang=Finnish
|
||||
allLocales.add(new Locale("fr", "FR")); // France / lang=French
|
||||
allLocales.add(new Locale("fr", "GF")); // French Guiana / lang=French
|
||||
allLocales.add(new Locale("fr", "PF")); // French Polynesia / lang=French
|
||||
allLocales.add(new Locale("fr", "TF")); // French Southern Territories / lang=French
|
||||
allLocales.add(new Locale("fr", "GA")); // Gabon / lang=French
|
||||
allLocales.add(new Locale("en", "GM")); // Gambia / lang=English
|
||||
allLocales.add(new Locale("ka", "GE")); // Georgia / lang=Georgian
|
||||
allLocales.add(new Locale("de", "DE")); // Germany / lang=German
|
||||
allLocales.add(new Locale("en", "GH")); // Ghana / lang=English
|
||||
allLocales.add(new Locale("en", "GI")); // Gibraltar / lang=English
|
||||
allLocales.add(new Locale("el", "GR")); // Greece / lang=Greek (modern)
|
||||
allLocales.add(new Locale("kl", "GL")); // Greenland / lang=Kalaallisut
|
||||
allLocales.add(new Locale("en", "GD")); // Grenada / lang=English
|
||||
allLocales.add(new Locale("fr", "GP")); // Guadeloupe / lang=French
|
||||
allLocales.add(new Locale("en", "GU")); // Guam / lang=English
|
||||
allLocales.add(new Locale("es", "GT")); // Guatemala / lang=Spanish
|
||||
allLocales.add(new Locale("en", "GG")); // Guernsey / lang=English
|
||||
allLocales.add(new Locale("fr", "GN")); // Guinea / lang=French
|
||||
allLocales.add(new Locale("pt", "GW")); // Guinea-Bissau / lang=Portuguese
|
||||
allLocales.add(new Locale("en", "GY")); // Guyana / lang=English
|
||||
allLocales.add(new Locale("fr", "HT")); // Haiti / lang=French
|
||||
allLocales.add(new Locale("la", "VA")); // Holy See / lang=Latin
|
||||
allLocales.add(new Locale("es", "HN")); // Honduras / lang=Spanish
|
||||
allLocales.add(new Locale("en", "HK")); // Hong Kong / lang=English
|
||||
allLocales.add(new Locale("hu", "HU")); // Hungary / lang=Hungarian
|
||||
allLocales.add(new Locale("is", "IS")); // Iceland / lang=Icelandic
|
||||
allLocales.add(new Locale("hi", "IN")); // India / lang=Hindi
|
||||
allLocales.add(new Locale("id", "ID")); // Indonesia / lang=Indonesian
|
||||
allLocales.add(new Locale("fr", "CI")); // Côte d'Ivoire / lang=French
|
||||
allLocales.add(new Locale("fa", "IR")); // Iran (Islamic Republic of) / lang=Persian (Farsi)
|
||||
allLocales.add(new Locale("ar", "IQ")); // Iraq / lang=Arabic
|
||||
allLocales.add(new Locale("ga", "IE")); // Ireland / lang=Irish
|
||||
allLocales.add(new Locale("en", "IM")); // Isle of Man / lang=English
|
||||
allLocales.add(new Locale("he", "IL")); // Israel / lang=Hebrew (modern)
|
||||
allLocales.add(new Locale("it", "IT")); // Italy / lang=Italian
|
||||
allLocales.add(new Locale("en", "JM")); // Jamaica / lang=English
|
||||
allLocales.add(new Locale("ja", "JP")); // Japan / lang=Japanese
|
||||
allLocales.add(new Locale("en", "JE")); // Jersey / lang=English
|
||||
allLocales.add(new Locale("ar", "JO")); // Jordan / lang=Arabic
|
||||
allLocales.add(new Locale("kk", "KZ")); // Kazakhstan / lang=Kazakh
|
||||
allLocales.add(new Locale("en", "KE")); // Kenya / lang=English
|
||||
allLocales.add(new Locale("en", "KI")); // Kiribati / lang=English
|
||||
allLocales.add(new Locale("ar", "KW")); // Kuwait / lang=Arabic
|
||||
allLocales.add(new Locale("ky", "KG")); // Kyrgyzstan / lang=Kyrgyz
|
||||
allLocales.add(new Locale("lo", "LA")); // Lao People's Democratic Republic / lang=Lao
|
||||
allLocales.add(new Locale("lv", "LV")); // Latvia / lang=Latvian
|
||||
allLocales.add(new Locale("ar", "LB")); // Lebanon / lang=Arabic
|
||||
allLocales.add(new Locale("en", "LS")); // Lesotho / lang=English
|
||||
allLocales.add(new Locale("en", "LR")); // Liberia / lang=English
|
||||
allLocales.add(new Locale("ar", "LY")); // Libya / lang=Arabic
|
||||
allLocales.add(new Locale("de", "LI")); // Liechtenstein / lang=German
|
||||
allLocales.add(new Locale("lt", "LT")); // Lithuania / lang=Lithuanian
|
||||
allLocales.add(new Locale("fr", "LU")); // Luxembourg / lang=French
|
||||
allLocales.add(new Locale("zh", "MO")); // Macao / lang=Chinese
|
||||
allLocales.add(new Locale("mk", "MK")); // Macedonia (the former Yugoslav Republic of) / lang=Macedonian
|
||||
allLocales.add(new Locale("fr", "MG")); // Madagascar / lang=French
|
||||
allLocales.add(new Locale("en", "MW")); // Malawi / lang=English
|
||||
allLocales.add(new Locale("en", "MY")); // Malaysia / lang=Malaysian
|
||||
allLocales.add(new Locale("dv", "MV")); // Maldives / lang=Divehi
|
||||
allLocales.add(new Locale("fr", "ML")); // Mali / lang=French
|
||||
allLocales.add(new Locale("mt", "MT")); // Malta / lang=Maltese
|
||||
allLocales.add(new Locale("en", "MH")); // Marshall Islands / lang=English
|
||||
allLocales.add(new Locale("fr", "MQ")); // Martinique / lang=French
|
||||
allLocales.add(new Locale("ar", "MR")); // Mauritania / lang=Arabic
|
||||
allLocales.add(new Locale("en", "MU")); // Mauritius / lang=English
|
||||
allLocales.add(new Locale("fr", "YT")); // Mayotte / lang=French
|
||||
allLocales.add(new Locale("es", "MX")); // Mexico / lang=Spanish
|
||||
allLocales.add(new Locale("en", "FM")); // Micronesia (Federated States of) / lang=English
|
||||
allLocales.add(new Locale("ro", "MD")); // Moldova (Republic of) / lang=Romanian
|
||||
allLocales.add(new Locale("fr", "MC")); // Monaco / lang=French
|
||||
allLocales.add(new Locale("mn", "MN")); // Mongolia / lang=Mongolian
|
||||
allLocales.add(new Locale("sr", "ME")); // Montenegro / lang=Serbian
|
||||
allLocales.add(new Locale("en", "MS")); // Montserrat / lang=English
|
||||
allLocales.add(new Locale("ar", "MA")); // Morocco / lang=Arabic
|
||||
allLocales.add(new Locale("pt", "MZ")); // Mozambique / lang=Portuguese
|
||||
allLocales.add(new Locale("my", "MM")); // Myanmar / lang=Burmese
|
||||
allLocales.add(new Locale("en", "NA")); // Namibia / lang=English
|
||||
allLocales.add(new Locale("en", "NR")); // Nauru / lang=English
|
||||
allLocales.add(new Locale("ne", "NP")); // Nepal / lang=Nepali
|
||||
allLocales.add(new Locale("nl", "NL")); // Netherlands / lang=Dutch
|
||||
allLocales.add(new Locale("fr", "NC")); // New Caledonia / lang=French
|
||||
allLocales.add(new Locale("en", "NZ")); // New Zealand / lang=English
|
||||
allLocales.add(new Locale("es", "NI")); // Nicaragua / lang=Spanish
|
||||
allLocales.add(new Locale("fr", "NE")); // Niger / lang=French
|
||||
allLocales.add(new Locale("en", "NG")); // Nigeria / lang=English
|
||||
allLocales.add(new Locale("en", "NU")); // Niue / lang=English
|
||||
allLocales.add(new Locale("en", "NF")); // Norfolk Island / lang=English
|
||||
allLocales.add(new Locale("ko", "KP")); // Korea (Democratic People's Republic of) / lang=Korean
|
||||
allLocales.add(new Locale("en", "MP")); // Northern Mariana Islands / lang=English
|
||||
allLocales.add(new Locale("no", "NO")); // Norway / lang=Norwegian
|
||||
allLocales.add(new Locale("ar", "OM")); // Oman / lang=Arabic
|
||||
allLocales.add(new Locale("en", "PK")); // Pakistan / lang=English
|
||||
allLocales.add(new Locale("en", "PW")); // Palau / lang=English
|
||||
allLocales.add(new Locale("ar", "PS")); // Palestine, State of / lang=Arabic
|
||||
allLocales.add(new Locale("es", "PA")); // Panama / lang=Spanish
|
||||
allLocales.add(new Locale("en", "PG")); // Papua New Guinea / lang=English
|
||||
allLocales.add(new Locale("es", "PY")); // Paraguay / lang=Spanish
|
||||
allLocales.add(new Locale("es", "PE")); // Peru / lang=Spanish
|
||||
allLocales.add(new Locale("en", "PH")); // Philippines / lang=English
|
||||
allLocales.add(new Locale("en", "PN")); // Pitcairn / lang=English
|
||||
allLocales.add(new Locale("pl", "PL")); // Poland / lang=Polish
|
||||
allLocales.add(new Locale("pt", "PT")); // Portugal / lang=Portuguese
|
||||
allLocales.add(new Locale("es", "PR")); // Puerto Rico / lang=Spanish
|
||||
allLocales.add(new Locale("ar", "QA")); // Qatar / lang=Arabic
|
||||
allLocales.add(new Locale("sq", "XK")); // Republic of Kosovo / lang=Albanian
|
||||
allLocales.add(new Locale("fr", "RE")); // Réunion / lang=French
|
||||
allLocales.add(new Locale("ro", "RO")); // Romania / lang=Romanian
|
||||
allLocales.add(new Locale("ru", "RU")); // Russian Federation / lang=Russian
|
||||
allLocales.add(new Locale("rw", "RW")); // Rwanda / lang=Kinyarwanda
|
||||
allLocales.add(new Locale("fr", "BL")); // Saint Barthélemy / lang=French
|
||||
allLocales.add(new Locale("en", "SH")); // Saint Helena, Ascension and Tristan da Cunha / lang=English
|
||||
allLocales.add(new Locale("en", "KN")); // Saint Kitts and Nevis / lang=English
|
||||
allLocales.add(new Locale("en", "LC")); // Saint Lucia / lang=English
|
||||
allLocales.add(new Locale("en", "MF")); // Saint Martin (French part) / lang=English
|
||||
allLocales.add(new Locale("fr", "PM")); // Saint Pierre and Miquelon / lang=French
|
||||
allLocales.add(new Locale("en", "VC")); // Saint Vincent and the Grenadines / lang=English
|
||||
allLocales.add(new Locale("sm", "WS")); // Samoa / lang=Samoan
|
||||
allLocales.add(new Locale("it", "SM")); // San Marino / lang=Italian
|
||||
allLocales.add(new Locale("pt", "ST")); // Sao Tome and Principe / lang=Portuguese
|
||||
allLocales.add(new Locale("ar", "SA")); // Saudi Arabia / lang=Arabic
|
||||
allLocales.add(new Locale("fr", "SN")); // Senegal / lang=French
|
||||
allLocales.add(new Locale("sr", "RS")); // Serbia / lang=Serbian
|
||||
allLocales.add(new Locale("fr", "SC")); // Seychelles / lang=French
|
||||
allLocales.add(new Locale("en", "SL")); // Sierra Leone / lang=English
|
||||
allLocales.add(new Locale("en", "SG")); // Singapore / lang=English
|
||||
allLocales.add(new Locale("nl", "SX")); // Sint Maarten (Dutch part) / lang=Dutch
|
||||
allLocales.add(new Locale("sk", "SK")); // Slovakia / lang=Slovak
|
||||
allLocales.add(new Locale("sl", "SI")); // Slovenia / lang=Slovene
|
||||
allLocales.add(new Locale("en", "SB")); // Solomon Islands / lang=English
|
||||
allLocales.add(new Locale("so", "SO")); // Somalia / lang=Somali
|
||||
allLocales.add(new Locale("af", "ZA")); // South Africa / lang=Afrikaans
|
||||
allLocales.add(new Locale("en", "GS")); // South Georgia and the South Sandwich Islands / lang=English
|
||||
allLocales.add(new Locale("ko", "KR")); // Korea (Republic of) / lang=Korean
|
||||
allLocales.add(new Locale("en", "SS")); // South Sudan / lang=English
|
||||
allLocales.add(new Locale("es", "ES")); // Spain / lang=Spanish
|
||||
allLocales.add(new Locale("si", "LK")); // Sri Lanka / lang=Sinhalese
|
||||
allLocales.add(new Locale("ar", "SD")); // Sudan / lang=Arabic
|
||||
allLocales.add(new Locale("nl", "SR")); // Suriname / lang=Dutch
|
||||
allLocales.add(new Locale("no", "SJ")); // Svalbard and Jan Mayen / lang=Norwegian
|
||||
allLocales.add(new Locale("en", "SZ")); // Swaziland / lang=English
|
||||
allLocales.add(new Locale("sv", "SE")); // Sweden / lang=Swedish
|
||||
allLocales.add(new Locale("de", "CH")); // Switzerland / lang=German
|
||||
allLocales.add(new Locale("ar", "SY")); // Syrian Arab Republic / lang=Arabic
|
||||
allLocales.add(new Locale("zh", "TW")); // Taiwan / lang=Chinese
|
||||
allLocales.add(new Locale("tg", "TJ")); // Tajikistan / lang=Tajik
|
||||
allLocales.add(new Locale("sw", "TZ")); // Tanzania, United Republic of / lang=Swahili
|
||||
allLocales.add(new Locale("th", "TH")); // Thailand / lang=Thai
|
||||
allLocales.add(new Locale("pt", "TL")); // Timor-Leste / lang=Portuguese
|
||||
allLocales.add(new Locale("fr", "TG")); // Togo / lang=French
|
||||
allLocales.add(new Locale("en", "TK")); // Tokelau / lang=English
|
||||
allLocales.add(new Locale("en", "TO")); // Tonga / lang=English
|
||||
allLocales.add(new Locale("en", "TT")); // Trinidad and Tobago / lang=English
|
||||
allLocales.add(new Locale("ar", "TN")); // Tunisia / lang=Arabic
|
||||
allLocales.add(new Locale("tr", "TR")); // Turkey / lang=Turkish
|
||||
allLocales.add(new Locale("tk", "TM")); // Turkmenistan / lang=Turkmen
|
||||
allLocales.add(new Locale("en", "TC")); // Turks and Caicos Islands / lang=English
|
||||
allLocales.add(new Locale("en", "TV")); // Tuvalu / lang=English
|
||||
allLocales.add(new Locale("en", "UG")); // Uganda / lang=English
|
||||
allLocales.add(new Locale("uk", "UA")); // Ukraine / lang=Ukrainian
|
||||
allLocales.add(new Locale("ar", "AE")); // United Arab Emirates / lang=Arabic
|
||||
allLocales.add(new Locale("en", "GB")); // United Kingdom of Great Britain and Northern Ireland / lang=English
|
||||
allLocales.add(new Locale("en", "US")); // United States of America / lang=English
|
||||
allLocales.add(new Locale("es", "UY")); // Uruguay / lang=Spanish
|
||||
allLocales.add(new Locale("uz", "UZ")); // Uzbekistan / lang=Uzbek
|
||||
allLocales.add(new Locale("bi", "VU")); // Vanuatu / lang=Bislama
|
||||
allLocales.add(new Locale("es", "VE")); // Venezuela (Bolivarian Republic of) / lang=Spanish
|
||||
allLocales.add(new Locale("vi", "VN")); // Viet Nam / lang=Vietnamese
|
||||
allLocales.add(new Locale("fr", "WF")); // Wallis and Futuna / lang=French
|
||||
allLocales.add(new Locale("es", "EH")); // Western Sahara / lang=Spanish
|
||||
allLocales.add(new Locale("ar", "YE")); // Yemen / lang=Arabic
|
||||
allLocales.add(new Locale("en", "ZM")); // Zambia / lang=English
|
||||
allLocales.add(new Locale("en", "ZW")); // Zimbabwe / lang=English
|
||||
|
||||
return allLocales;
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.proto.persistable.PersistablePayload;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public final class Region implements PersistablePayload {
|
||||
public final String code;
|
||||
public final String name;
|
||||
|
||||
public Region(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return PB.Region.newBuilder().setCode(code).setName(name).build();
|
||||
}
|
||||
|
||||
public static Region fromProto(PB.Region proto) {
|
||||
return new Region(proto.getCode(), proto.getName());
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import io.bisq.common.GlobalSettings;
|
||||
import io.bisq.common.app.DevEnv;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class Res {
|
||||
private static final Logger log = LoggerFactory.getLogger(Res.class);
|
||||
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
private static ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n.displayStrings", GlobalSettings.getLocale(), new UTF8Control());
|
||||
|
||||
static {
|
||||
GlobalSettings.localeProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if ("en".equalsIgnoreCase(newValue.getLanguage()))
|
||||
newValue = Locale.ROOT;
|
||||
resourceBundle = ResourceBundle.getBundle("i18n.displayStrings", newValue, new UTF8Control());
|
||||
});
|
||||
}
|
||||
|
||||
public static String getWithCol(String key) {
|
||||
return get(key) + ":";
|
||||
}
|
||||
|
||||
public static String getWithColAndCap(String key) {
|
||||
return StringUtils.capitalize(get(key)) + ":";
|
||||
}
|
||||
|
||||
public static ResourceBundle getResourceBundle() {
|
||||
return resourceBundle;
|
||||
}
|
||||
|
||||
private static String baseCurrencyCode;
|
||||
private static String baseCurrencyName;
|
||||
private static String baseCurrencyNameLowerCase;
|
||||
|
||||
public static void setBaseCurrencyCode(String baseCurrencyCode) {
|
||||
Res.baseCurrencyCode = baseCurrencyCode;
|
||||
}
|
||||
|
||||
public static void setBaseCurrencyName(String baseCurrencyName) {
|
||||
Res.baseCurrencyName = baseCurrencyName;
|
||||
baseCurrencyNameLowerCase = baseCurrencyName.toLowerCase();
|
||||
}
|
||||
|
||||
public static String getBaseCurrencyCode() {
|
||||
return baseCurrencyCode;
|
||||
}
|
||||
|
||||
public static String getBaseCurrencyName() {
|
||||
return baseCurrencyName;
|
||||
}
|
||||
|
||||
// Capitalize first character
|
||||
public static String getWithCap(String key) {
|
||||
return StringUtils.capitalize(get(key));
|
||||
}
|
||||
|
||||
public static String getWithCol(String key, Object... arguments) {
|
||||
return get(key, arguments) + ":";
|
||||
}
|
||||
|
||||
public static String get(String key, Object... arguments) {
|
||||
return MessageFormat.format(Res.get(key), arguments);
|
||||
}
|
||||
|
||||
public static String get(String key) {
|
||||
try {
|
||||
return resourceBundle.getString(key)
|
||||
.replace("BTC", baseCurrencyCode)
|
||||
.replace("Bitcoin", baseCurrencyName)
|
||||
.replace("bitcoin", baseCurrencyNameLowerCase);
|
||||
} catch (MissingResourceException e) {
|
||||
log.warn("Missing resource for key: " + key);
|
||||
if (DevEnv.isDevMode())
|
||||
throw new RuntimeException("Missing resource for key: " + key);
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds UTF8 support for property files
|
||||
class UTF8Control extends ResourceBundle.Control {
|
||||
|
||||
public ResourceBundle newBundle(String baseName, @NotNull Locale locale, @NotNull String format, ClassLoader loader, boolean reload)
|
||||
throws IllegalAccessException, InstantiationException, IOException {
|
||||
// The below is a copy of the default implementation.
|
||||
final String bundleName = toBundleName(baseName, locale);
|
||||
final String resourceName = toResourceName(bundleName, "properties");
|
||||
ResourceBundle bundle = null;
|
||||
InputStream stream = null;
|
||||
if (reload) {
|
||||
final URL url = loader.getResource(resourceName);
|
||||
if (url != null) {
|
||||
final URLConnection connection = url.openConnection();
|
||||
if (connection != null) {
|
||||
connection.setUseCaches(false);
|
||||
stream = connection.getInputStream();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stream = loader.getResourceAsStream(resourceName);
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
// Only this line is changed to make it to read properties files as UTF-8.
|
||||
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import io.bisq.common.proto.ProtobufferException;
|
||||
import io.bisq.common.proto.persistable.PersistablePayload;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@Getter
|
||||
@Slf4j
|
||||
public abstract class TradeCurrency implements PersistablePayload, Comparable<TradeCurrency> {
|
||||
protected final String code;
|
||||
protected final String name;
|
||||
|
||||
public TradeCurrency(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static TradeCurrency fromProto(PB.TradeCurrency proto) {
|
||||
switch (proto.getMessageCase()) {
|
||||
case FIAT_CURRENCY:
|
||||
return FiatCurrency.fromProto(proto);
|
||||
case CRYPTO_CURRENCY:
|
||||
return CryptoCurrency.fromProto(proto);
|
||||
default:
|
||||
throw new ProtobufferException("Unknown message case: " + proto.getMessageCase());
|
||||
}
|
||||
}
|
||||
|
||||
public PB.TradeCurrency.Builder getTradeCurrencyBuilder() {
|
||||
return PB.TradeCurrency.newBuilder()
|
||||
.setCode(code)
|
||||
.setName(name);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getDisplayPrefix() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getNameAndCode() {
|
||||
return name + " (" + code + ")";
|
||||
}
|
||||
|
||||
public String getCodeAndName() {
|
||||
return code + " (" + name + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull TradeCurrency other) {
|
||||
return this.name.compareTo(other.name);
|
||||
}
|
||||
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
package io.bisq.common.monetary;
|
||||
|
||||
import com.google.common.math.LongMath;
|
||||
import org.bitcoinj.core.Monetary;
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* Cloned from Fiat class and altered SMALLEST_UNIT_EXPONENT as Fiat is final.
|
||||
* <p/>
|
||||
* Represents a monetary fiat value. It was decided to not fold this into {@link org.bitcoinj.core.Coin} because of type
|
||||
* safety. Volume values always come with an attached currency code.
|
||||
* <p/>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public final class Altcoin implements Monetary, Comparable<Altcoin> {
|
||||
/**
|
||||
* The absolute value of exponent of the value of a "smallest unit" in scientific notation. We picked 4 rather than
|
||||
* 2, because in financial applications it's common to use sub-cent precision.
|
||||
*/
|
||||
public static final int SMALLEST_UNIT_EXPONENT = 8;
|
||||
private static final MonetaryFormat FRIENDLY_FORMAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1).postfixCode();
|
||||
private static final MonetaryFormat PLAIN_FORMAT = new MonetaryFormat().shift(0).minDecimals(0).repeatOptionalDecimals(1, 8).noCode();
|
||||
|
||||
/**
|
||||
* The number of smallest units of this monetary value.
|
||||
*/
|
||||
public final long value;
|
||||
public final String currencyCode;
|
||||
|
||||
private Altcoin(final String currencyCode, final long value) {
|
||||
this.value = value;
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
public static Altcoin valueOf(final String currencyCode, final long value) {
|
||||
return new Altcoin(currencyCode, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int smallestUnitExponent() {
|
||||
return SMALLEST_UNIT_EXPONENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of "smallest units" of this monetary value.
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return currencyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an amount expressed in the way humans are used to.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* This takes string in a format understood by {@link BigDecimal#BigDecimal(String)}, for example "0", "1", "0.10",
|
||||
* "1.23E3", "1234.5E-5".
|
||||
*
|
||||
* @throws IllegalArgumentException if you try to specify fractional satoshis, or a value out of range.
|
||||
*/
|
||||
public static Altcoin parseAltcoin(final String currencyCode, String inputValue) {
|
||||
inputValue = inputValue.replace(",", ".");
|
||||
try {
|
||||
long val = new BigDecimal(inputValue).movePointRight(SMALLEST_UNIT_EXPONENT)
|
||||
.toBigIntegerExact().longValue();
|
||||
return Altcoin.valueOf(currencyCode, val);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Altcoin add(final Altcoin value) {
|
||||
checkArgument(value.currencyCode.equals(currencyCode));
|
||||
return new Altcoin(currencyCode, LongMath.checkedAdd(this.value, value.value));
|
||||
}
|
||||
|
||||
public Altcoin subtract(final Altcoin value) {
|
||||
checkArgument(value.currencyCode.equals(currencyCode));
|
||||
return new Altcoin(currencyCode, LongMath.checkedSubtract(this.value, value.value));
|
||||
}
|
||||
|
||||
public Altcoin multiply(final long factor) {
|
||||
return new Altcoin(currencyCode, LongMath.checkedMultiply(this.value, factor));
|
||||
}
|
||||
|
||||
public Altcoin divide(final long divisor) {
|
||||
return new Altcoin(currencyCode, this.value / divisor);
|
||||
}
|
||||
|
||||
public Altcoin[] divideAndRemainder(final long divisor) {
|
||||
return new Altcoin[]{new Altcoin(currencyCode, this.value / divisor), new Altcoin(currencyCode, this.value % divisor)};
|
||||
}
|
||||
|
||||
public long divide(final Altcoin divisor) {
|
||||
checkArgument(divisor.currencyCode.equals(currencyCode));
|
||||
return this.value / divisor.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value greater than zero, otherwise false.
|
||||
*/
|
||||
public boolean isPositive() {
|
||||
return signum() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value less than zero, otherwise false.
|
||||
*/
|
||||
public boolean isNegative() {
|
||||
return signum() == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents zero monetary value, otherwise false.
|
||||
*/
|
||||
public boolean isZero() {
|
||||
return signum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is greater than that of the given other Coin,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isGreaterThan(Altcoin other) {
|
||||
return compareTo(other) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is less than that of the given other Coin,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isLessThan(Altcoin other) {
|
||||
return compareTo(other) < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int signum() {
|
||||
if (this.value == 0)
|
||||
return 0;
|
||||
return this.value < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
public Altcoin negate() {
|
||||
return new Altcoin(currencyCode, -this.value);
|
||||
}
|
||||
|
||||
public String toFriendlyString() {
|
||||
return FRIENDLY_FORMAT.code(0, currencyCode).format(this).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the value as a plain string denominated in BTC. The result is unformatted with no trailing zeroes. For
|
||||
* instance, a value of 150000 satoshis gives an output string of "0.0015" BTC
|
||||
* </p>
|
||||
*/
|
||||
public String toPlainString() {
|
||||
return PLAIN_FORMAT.format(this).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (o == null || o.getClass() != getClass())
|
||||
return false;
|
||||
final Altcoin other = (Altcoin) o;
|
||||
return this.value == other.value && this.currencyCode.equals(other.currencyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) this.value + 37 * this.currencyCode.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final Altcoin other) {
|
||||
if (!this.currencyCode.equals(other.currencyCode))
|
||||
return this.currencyCode.compareTo(other.currencyCode);
|
||||
if (this.value != other.value)
|
||||
return this.value > other.value ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package io.bisq.common.monetary;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
// Cloned from ExchangeRate. Use Altcoin instead of Fiat.
|
||||
@Slf4j
|
||||
public class AltcoinExchangeRate {
|
||||
/**
|
||||
* An exchange rate is expressed as a ratio of a {@link Coin} and a {@link Altcoin} amount.
|
||||
*/
|
||||
|
||||
public final Coin coin;
|
||||
public final Altcoin altcoin;
|
||||
|
||||
/**
|
||||
* Construct exchange rate. This amount of coin is worth that amount of altcoin.
|
||||
*/
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public AltcoinExchangeRate(Coin coin, Altcoin altcoin) {
|
||||
checkArgument(coin.isPositive());
|
||||
checkArgument(altcoin.isPositive());
|
||||
checkArgument(altcoin.currencyCode != null, "currency code required");
|
||||
this.coin = coin;
|
||||
this.altcoin = altcoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct exchange rate. One coin is worth this amount of altcoin.
|
||||
*/
|
||||
public AltcoinExchangeRate(Altcoin altcoin) {
|
||||
this(Coin.COIN, altcoin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a coin amount to a altcoin amount using this exchange rate.
|
||||
*
|
||||
* @throws ArithmeticException if the converted altcoin amount is too high or too low.
|
||||
*/
|
||||
public Altcoin coinToAltcoin(Coin convertCoin) {
|
||||
BigInteger converted = BigInteger.valueOf(coin.value)
|
||||
.multiply(BigInteger.valueOf(convertCoin.value))
|
||||
.divide(BigInteger.valueOf(altcoin.value));
|
||||
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|
||||
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
|
||||
throw new ArithmeticException("Overflow");
|
||||
return Altcoin.valueOf(altcoin.currencyCode, converted.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a altcoin amount to a coin amount using this exchange rate.
|
||||
*
|
||||
* @throws ArithmeticException if the converted coin amount is too high or too low.
|
||||
*/
|
||||
public Coin altcoinToCoin(Altcoin convertAltcoin) {
|
||||
checkArgument(convertAltcoin.currencyCode.equals(altcoin.currencyCode), "Currency mismatch: %s vs %s",
|
||||
convertAltcoin.currencyCode, altcoin.currencyCode);
|
||||
// Use BigInteger because it's much easier to maintain full precision without overflowing.
|
||||
BigInteger converted = BigInteger.valueOf(altcoin.value)
|
||||
.multiply(BigInteger.valueOf(convertAltcoin.value))
|
||||
.divide(BigInteger.valueOf(coin.value));
|
||||
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|
||||
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
|
||||
throw new ArithmeticException("Overflow");
|
||||
try {
|
||||
return Coin.valueOf(converted.longValue());
|
||||
} catch (IllegalArgumentException x) {
|
||||
throw new ArithmeticException("Overflow: " + x.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package io.bisq.common.monetary;
|
||||
|
||||
import org.bitcoinj.core.Monetary;
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class MonetaryWrapper {
|
||||
private static final Logger log = LoggerFactory.getLogger(MonetaryWrapper.class);
|
||||
|
||||
/// Instance of Fiat or Altcoin
|
||||
protected final Monetary monetary;
|
||||
protected final MonetaryFormat fiatFormat = MonetaryFormat.FIAT.repeatOptionalDecimals(0, 0);
|
||||
protected final MonetaryFormat altCoinFormat = MonetaryFormat.FIAT.repeatOptionalDecimals(0, 0);
|
||||
|
||||
public MonetaryWrapper(Monetary monetary) {
|
||||
this.monetary = monetary;
|
||||
}
|
||||
|
||||
public Monetary getMonetary() {
|
||||
return monetary;
|
||||
}
|
||||
|
||||
public boolean isZero() {
|
||||
return monetary.getValue() == 0;
|
||||
}
|
||||
|
||||
public int smallestUnitExponent() {
|
||||
return monetary.smallestUnitExponent();
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return monetary.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (o == null || o.getClass() != getClass())
|
||||
return false;
|
||||
final Monetary otherMonetary = ((MonetaryWrapper) o).getMonetary();
|
||||
return monetary.getValue() == otherMonetary.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) monetary.getValue();
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package io.bisq.common.monetary;
|
||||
|
||||
import io.bisq.common.locale.CurrencyUtil;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Monetary;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Wrapper for price values with variable precision. If monetary is Altcoin we use precision 8 otherwise Fiat with precision 4.
|
||||
* The inverted price notation in the offer will be refactored once in a bigger refactoring update.
|
||||
*/
|
||||
public class Price extends MonetaryWrapper implements Comparable<Price> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Price.class);
|
||||
|
||||
public Price(Monetary monetary) {
|
||||
super(monetary);
|
||||
}
|
||||
|
||||
public static Price parse(String currencyCode, String inputValue) {
|
||||
final String cleaned = inputValue.replace(",", ".");
|
||||
if (CurrencyUtil.isFiatCurrency(currencyCode))
|
||||
return new Price(Fiat.parseFiat(currencyCode, cleaned));
|
||||
else
|
||||
return new Price(Altcoin.parseAltcoin(currencyCode, cleaned));
|
||||
}
|
||||
|
||||
public static Price valueOf(String currencyCode, long value) {
|
||||
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
|
||||
return new Price(Fiat.valueOf(currencyCode, value));
|
||||
} else {
|
||||
return new Price(Altcoin.valueOf(currencyCode, value));
|
||||
}
|
||||
}
|
||||
|
||||
public Volume getVolumeByAmount(Coin amount) {
|
||||
if (monetary instanceof Fiat)
|
||||
return new Volume(new ExchangeRate((Fiat) monetary).coinToFiat(amount));
|
||||
else if (monetary instanceof Altcoin)
|
||||
return new Volume(new AltcoinExchangeRate((Altcoin) monetary).coinToAltcoin(amount));
|
||||
else
|
||||
throw new IllegalStateException("Monetary must be either of type Fiat or Altcoin");
|
||||
}
|
||||
|
||||
public Coin getAmountByVolume(Volume volume) {
|
||||
Monetary monetary = volume.getMonetary();
|
||||
if (monetary instanceof Fiat && this.monetary instanceof Fiat)
|
||||
return new ExchangeRate((Fiat) this.monetary).fiatToCoin((Fiat) monetary);
|
||||
else if (monetary instanceof Altcoin && this.monetary instanceof Altcoin)
|
||||
return new AltcoinExchangeRate((Altcoin) this.monetary).altcoinToCoin((Altcoin) monetary);
|
||||
else
|
||||
return Coin.ZERO;
|
||||
}
|
||||
|
||||
private static int getPrecision(String currencyCode) {
|
||||
return CurrencyUtil.isCryptoCurrency(currencyCode) ? 8 : 4;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).getCurrencyCode() : ((Fiat) monetary).getCurrencyCode();
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return monetary.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Price other) {
|
||||
if (!this.getCurrencyCode().equals(other.getCurrencyCode()))
|
||||
return this.getCurrencyCode().compareTo(other.getCurrencyCode());
|
||||
if (this.getValue() != other.getValue())
|
||||
return this.getValue() > other.getValue() ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isPositive() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).isPositive() : ((Fiat) monetary).isPositive();
|
||||
}
|
||||
|
||||
public Price subtract(Price other) {
|
||||
if (monetary instanceof Altcoin) {
|
||||
return new Price(((Altcoin) monetary).subtract((Altcoin) other.monetary));
|
||||
} else {
|
||||
return new Price(((Fiat) monetary).subtract((Fiat) other.monetary));
|
||||
}
|
||||
}
|
||||
|
||||
public String toFriendlyString() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).toFriendlyString() : ((Fiat) monetary).toFriendlyString();
|
||||
}
|
||||
|
||||
public String toPlainString() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).toPlainString() : ((Fiat) monetary).toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toPlainString();
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package io.bisq.common.monetary;
|
||||
|
||||
import io.bisq.common.locale.CurrencyUtil;
|
||||
import org.bitcoinj.core.Monetary;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Volume extends MonetaryWrapper implements Comparable<Volume> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Volume.class);
|
||||
|
||||
public Volume(Monetary monetary) {
|
||||
super(monetary);
|
||||
}
|
||||
|
||||
public static Volume parse(String inputValue, String currencyCode) {
|
||||
final String cleaned = inputValue.replace(",", ".");
|
||||
if (CurrencyUtil.isFiatCurrency(currencyCode))
|
||||
return new Volume(Fiat.parseFiat(currencyCode, cleaned));
|
||||
else
|
||||
return new Volume(Altcoin.parseAltcoin(currencyCode, cleaned));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Volume other) {
|
||||
if (!this.getCurrencyCode().equals(other.getCurrencyCode()))
|
||||
return this.getCurrencyCode().compareTo(other.getCurrencyCode());
|
||||
if (this.getValue() != other.getValue())
|
||||
return this.getValue() > other.getValue() ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).getCurrencyCode() : ((Fiat) monetary).getCurrencyCode();
|
||||
}
|
||||
|
||||
public String toPlainString() {
|
||||
return monetary instanceof Altcoin ? ((Altcoin) monetary).toPlainString() : ((Fiat) monetary).toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toPlainString();
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto;
|
||||
|
||||
import io.bisq.common.Payload;
|
||||
import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
|
||||
public interface ProtoResolver {
|
||||
Payload fromProto(PB.PaymentAccountPayload proto);
|
||||
|
||||
PersistableEnvelope fromProto(PB.PersistableNetworkPayload proto);
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.Proto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class ProtoUtil {
|
||||
|
||||
public static Set<byte[]> byteSetFromProtoByteStringList(List<ByteString> byteStringList) {
|
||||
return byteStringList.stream().map(ByteString::toByteArray).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input String, except when it's the empty string: "", then null is returned.
|
||||
* Note: "" is the default value for a protobuffer string, so this means it's not filled in.
|
||||
*/
|
||||
@Nullable
|
||||
public static String stringOrNullFromProto(String proto) {
|
||||
return "".equals(proto) ? null : proto;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static byte[] byteArrayOrNullFromProto(ByteString proto) {
|
||||
return proto.isEmpty() ? null : proto.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Java enum from a Protobuf enum in a safe way.
|
||||
*
|
||||
* @param enumType the class of the enum, e.g: BlaEnum.class
|
||||
* @param name the name of the enum entry, e.g: proto.getWinner().name()
|
||||
* @param <E> the enum Type
|
||||
* @return an enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E enumFromProto(Class<E> enumType, String name) {
|
||||
E result = Enums.getIfPresent(enumType, name).orNull();
|
||||
if (result == null) {
|
||||
log.error("Invalid value for enum " + enumType.getSimpleName() + ": " + name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends Message> Iterable<T> collectionToProto(Collection<? extends Proto> collection) {
|
||||
return collection.stream()
|
||||
.map(e -> {
|
||||
final Message message = e.toProtoMessage();
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (T) message;
|
||||
} catch (Throwable t) {
|
||||
log.error("message could not be casted. message=" + message);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(e -> e != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> Iterable<T> collectionToProto(Collection<? extends Proto> collection, Function<? super Message, T> extra) {
|
||||
return collection.stream().map(o -> extra.apply(o.toProtoMessage())).collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto;
|
||||
|
||||
public class ProtobufferException extends RuntimeException {
|
||||
public ProtobufferException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ProtobufferException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.network;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.Envelope;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public abstract class NetworkEnvelope implements Envelope {
|
||||
|
||||
protected final int messageVersion;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected NetworkEnvelope(int messageVersion) {
|
||||
this.messageVersion = messageVersion;
|
||||
}
|
||||
|
||||
public PB.NetworkEnvelope.Builder getNetworkEnvelopeBuilder() {
|
||||
return PB.NetworkEnvelope.newBuilder().setMessageVersion(messageVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return getNetworkEnvelopeBuilder().build();
|
||||
}
|
||||
|
||||
// todo remove
|
||||
public PB.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||
return getNetworkEnvelopeBuilder().build();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int getMessageVersion() {
|
||||
// -1 is used for the case that we use an envelope message as payload (mailbox)
|
||||
// so we check only against 0 which is the default value if not set
|
||||
checkArgument(messageVersion != 0, "messageVersion is not set (0).");
|
||||
return messageVersion;
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package io.bisq.common.proto.network;
|
||||
|
||||
import io.bisq.common.Payload;
|
||||
|
||||
/**
|
||||
* Interface for objects used inside WireEnvelope or other WirePayloads.
|
||||
*/
|
||||
public interface NetworkPayload extends Payload {
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.network;
|
||||
|
||||
import io.bisq.common.proto.ProtoResolver;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
|
||||
public interface NetworkProtoResolver extends ProtoResolver {
|
||||
NetworkEnvelope fromProto(PB.NetworkEnvelope proto);
|
||||
|
||||
NetworkPayload fromProto(PB.StoragePayload proto);
|
||||
|
||||
NetworkPayload fromProto(PB.StorageEntryWrapper proto);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.*;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class NavigationPath implements PersistableEnvelope {
|
||||
private List<String> path = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
final PB.NavigationPath.Builder builder = PB.NavigationPath.newBuilder();
|
||||
if (!CollectionUtils.isEmpty(path)) builder.addAllPath(path);
|
||||
return PB.PersistableEnvelope.newBuilder().setNavigationPath(builder).build();
|
||||
}
|
||||
|
||||
public static PersistableEnvelope fromProto(PB.NavigationPath proto) {
|
||||
return new NavigationPath(new ArrayList<>(proto.getPathList()));
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import io.bisq.common.Envelope;
|
||||
|
||||
/**
|
||||
* Interface for the outside envelope object persisted to disc.
|
||||
*/
|
||||
public interface PersistableEnvelope extends Envelope {
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Delegate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PersistableHashMap<K, V extends PersistablePayload> implements PersistableEnvelope {
|
||||
@Delegate
|
||||
@Getter
|
||||
private Map<K, V> map = new HashMap<>();
|
||||
@Setter
|
||||
private Function<Map<K, V>, Message> toProto;
|
||||
|
||||
public PersistableHashMap(Map<K, V> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public PersistableHashMap(Map<K, V> map, Function<Map<K, V>, Message> toProto) {
|
||||
this(map);
|
||||
this.toProto = toProto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return toProto.apply(map);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Delegate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PersistableList<T extends PersistablePayload> implements PersistableEnvelope {
|
||||
@Delegate
|
||||
@Getter
|
||||
@Setter
|
||||
private List<T> list;
|
||||
@Setter
|
||||
private Function<List<T>, Message> toProto;
|
||||
|
||||
public PersistableList() {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
|
||||
public PersistableList(List<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public PersistableList(List<T> list, Function<List<T>, Message> toProto) {
|
||||
this(list);
|
||||
this.toProto = toProto;
|
||||
}
|
||||
|
||||
public PersistableList(HashSet<T> set) {
|
||||
this(set.stream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public PersistableList(HashSet<T> set, Function<List<T>, Message> toProto) {
|
||||
this(set);
|
||||
this.toProto = toProto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return toProto.apply(list);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import io.bisq.common.Payload;
|
||||
|
||||
/**
|
||||
* Interface for objects used inside Envelope or other Payloads.
|
||||
*/
|
||||
public interface PersistablePayload extends Payload {
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
public interface PersistedDataHost {
|
||||
void readPersisted();
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.proto.persistable;
|
||||
|
||||
import io.bisq.common.proto.ProtoResolver;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
|
||||
public interface PersistenceProtoResolver extends ProtoResolver {
|
||||
PersistableEnvelope fromProto(PB.PersistableEnvelope persistable);
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.storage;
|
||||
|
||||
import com.google.common.util.concurrent.CycleDetectingLockFactory;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Slf4j
|
||||
public class FileManager<T extends PersistableEnvelope> {
|
||||
private final File dir;
|
||||
private final File storageFile;
|
||||
private final ScheduledThreadPoolExecutor executor;
|
||||
private final AtomicBoolean savePending;
|
||||
private final long delay;
|
||||
private final Callable<Void> saveFileTask;
|
||||
private T persistable;
|
||||
private final PersistenceProtoResolver persistenceProtoResolver;
|
||||
private final ReentrantLock writeLock = CycleDetectingLockFactory.newInstance(CycleDetectingLockFactory.Policies.THROW).newReentrantLock("writeLock");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FileManager(File dir, File storageFile, long delay, PersistenceProtoResolver persistenceProtoResolver) {
|
||||
this.dir = dir;
|
||||
this.storageFile = storageFile;
|
||||
this.persistenceProtoResolver = persistenceProtoResolver;
|
||||
|
||||
executor = Utilities.getScheduledThreadPoolExecutor("FileManager", 1, 10, 5);
|
||||
|
||||
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
|
||||
savePending = new AtomicBoolean();
|
||||
this.delay = delay;
|
||||
|
||||
saveFileTask = () -> {
|
||||
try {
|
||||
Thread.currentThread().setName("Save-file-task-" + new Random().nextInt(10000));
|
||||
// Runs in an auto save thread.
|
||||
if (!savePending.getAndSet(false)) {
|
||||
// Some other scheduled request already beat us to it.
|
||||
return null;
|
||||
}
|
||||
saveNowInternal(persistable);
|
||||
} catch (Throwable e) {
|
||||
log.error("Error during saveFileTask", e);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
UserThread.execute(FileManager.this::shutDown);
|
||||
}, "FileManager.ShutDownHook"));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Actually write the wallet file to disk, using an atomic rename when possible. Runs on the current thread.
|
||||
*/
|
||||
public void saveNow(T persistable) {
|
||||
saveNowInternal(persistable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a save in the background. Useful for not very important wallet changes.
|
||||
*/
|
||||
public void saveLater(T persistable) {
|
||||
saveLater(persistable, delay);
|
||||
}
|
||||
|
||||
public void saveLater(T persistable, long delayInMilli) {
|
||||
this.persistable = persistable;
|
||||
|
||||
if (savePending.getAndSet(true))
|
||||
return; // Already pending.
|
||||
|
||||
executor.schedule(saveFileTask, delayInMilli, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized T read(File file) {
|
||||
log.debug("Read from disc: {}", file.getName());
|
||||
|
||||
try (final FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||
PB.PersistableEnvelope persistable = PB.PersistableEnvelope.parseDelimitedFrom(fileInputStream);
|
||||
return (T) persistenceProtoResolver.fromProto(persistable);
|
||||
} catch (Throwable t) {
|
||||
String errorMsg = "Exception at proto read: " + t.getMessage() + " file:" + file.getAbsolutePath();
|
||||
log.error(errorMsg, t);
|
||||
//if(DevEnv.DEV_MODE)
|
||||
throw new RuntimeException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeFile(String fileName) {
|
||||
log.debug("removeFile" + fileName);
|
||||
File file = new File(dir, fileName);
|
||||
boolean result = file.delete();
|
||||
if (!result)
|
||||
log.warn("Could not delete file: " + file.toString());
|
||||
|
||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||
if (backupDir.exists()) {
|
||||
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
|
||||
if (backupFile.exists()) {
|
||||
result = backupFile.delete();
|
||||
if (!result)
|
||||
log.warn("Could not delete backupFile: " + file.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shut down auto-saving.
|
||||
*/
|
||||
void shutDown() {
|
||||
executor.shutdown();
|
||||
try {
|
||||
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeAndBackupFile(String fileName) throws IOException {
|
||||
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "backup_of_corrupted_data").toString());
|
||||
if (!corruptedBackupDir.exists())
|
||||
if (!corruptedBackupDir.mkdir())
|
||||
log.warn("make dir failed");
|
||||
|
||||
File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "backup_of_corrupted_data", fileName).toString());
|
||||
FileUtil.renameFile(storageFile, corruptedFile);
|
||||
}
|
||||
|
||||
public synchronized void backupFile(String fileName, int numMaxBackupFiles) {
|
||||
FileUtil.rollingBackup(dir, fileName, numMaxBackupFiles);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void saveNowInternal(T persistable) {
|
||||
long now = System.currentTimeMillis();
|
||||
saveToFile(persistable, dir, storageFile);
|
||||
log.trace("Save {} completed in {} msec", storageFile, System.currentTimeMillis() - now);
|
||||
}
|
||||
|
||||
private synchronized void saveToFile(T persistable, File dir, File storageFile) {
|
||||
File tempFile = null;
|
||||
FileOutputStream fileOutputStream = null;
|
||||
PrintWriter printWriter = null;
|
||||
|
||||
try {
|
||||
log.debug("Write to disc: {}", storageFile.getName());
|
||||
PB.PersistableEnvelope protoPersistable;
|
||||
try {
|
||||
protoPersistable = (PB.PersistableEnvelope) persistable.toProtoMessage();
|
||||
if (protoPersistable.toByteArray().length == 0)
|
||||
log.error("protoPersistable is empty. persistable=" + persistable.getClass().getSimpleName());
|
||||
} catch (Throwable e) {
|
||||
log.error("Error in saveToFile toProtoMessage: {}, {}", persistable.getClass().getSimpleName(), storageFile);
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!dir.exists() && !dir.mkdir())
|
||||
log.warn("make dir failed");
|
||||
|
||||
tempFile = File.createTempFile("temp", null, dir);
|
||||
tempFile.deleteOnExit();
|
||||
fileOutputStream = new FileOutputStream(tempFile);
|
||||
|
||||
log.debug("Writing protobuffer class:{} to file:{}", persistable.getClass(), storageFile.getName());
|
||||
writeLock.lock();
|
||||
protoPersistable.writeDelimitedTo(fileOutputStream);
|
||||
|
||||
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
||||
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.getFD().sync();
|
||||
writeLock.unlock();
|
||||
|
||||
// Close resources before replacing file with temp file because otherwise it causes problems on windows
|
||||
// when rename temp file
|
||||
fileOutputStream.close();
|
||||
FileUtil.renameFile(tempFile, storageFile);
|
||||
} catch (Throwable t) {
|
||||
log.error("Error at saveToFile, storageFile=" + storageFile.toString(), t);
|
||||
} finally {
|
||||
if (writeLock.isLocked())
|
||||
writeLock.unlock();
|
||||
if (tempFile != null && tempFile.exists()) {
|
||||
log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + storageFile);
|
||||
if (!tempFile.delete())
|
||||
log.error("Cannot delete temp file.");
|
||||
}
|
||||
|
||||
try {
|
||||
if (fileOutputStream != null)
|
||||
fileOutputStream.close();
|
||||
//noinspection ConstantConditions,ConstantConditions
|
||||
if (printWriter != null)
|
||||
printWriter.close();
|
||||
} catch (IOException e) {
|
||||
// We swallow that
|
||||
e.printStackTrace();
|
||||
log.error("Cannot close resources." + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
package io.bisq.common.storage;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class FileUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
|
||||
|
||||
public static void rollingBackup(File dir, String fileName, int numMaxBackupFiles) {
|
||||
if (dir.exists()) {
|
||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||
if (!backupDir.exists())
|
||||
if (!backupDir.mkdir())
|
||||
log.warn("make dir failed.\nBackupDir=" + backupDir.getAbsolutePath());
|
||||
|
||||
File origFile = new File(Paths.get(dir.getAbsolutePath(), fileName).toString());
|
||||
if (origFile.exists()) {
|
||||
String dirName = "backups_" + fileName;
|
||||
if (dirName.contains("."))
|
||||
dirName = dirName.replace(".", "_");
|
||||
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
|
||||
if (!backupFileDir.exists())
|
||||
if (!backupFileDir.mkdir())
|
||||
log.warn("make backupFileDir failed.\nBackupFileDir=" + backupFileDir.getAbsolutePath());
|
||||
|
||||
File backupFile = new File(Paths.get(backupFileDir.getAbsolutePath(), new Date().getTime() + "_" + fileName).toString());
|
||||
|
||||
try {
|
||||
Files.copy(origFile, backupFile);
|
||||
|
||||
pruneBackup(backupFileDir, numMaxBackupFiles);
|
||||
} catch (IOException e) {
|
||||
log.error("Backup key failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void pruneBackup(File backupDir, int numMaxBackupFiles) {
|
||||
if (backupDir.isDirectory()) {
|
||||
File[] files = backupDir.listFiles();
|
||||
if (files != null) {
|
||||
List<File> filesList = Arrays.asList(files);
|
||||
if (filesList.size() > numMaxBackupFiles) {
|
||||
filesList.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
|
||||
File file = filesList.get(0);
|
||||
if (file.isFile()) {
|
||||
if (!file.delete())
|
||||
log.error("Failed to delete file: " + file);
|
||||
else
|
||||
pruneBackup(backupDir, numMaxBackupFiles);
|
||||
|
||||
} else {
|
||||
pruneBackup(new File(Paths.get(backupDir.getAbsolutePath(), file.getName()).toString()), numMaxBackupFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File file) throws IOException {
|
||||
deleteDirectory(file, null, true);
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File file, @Nullable File exclude, boolean ignoreLockedFiles) throws IOException {
|
||||
boolean excludeFileFound = false;
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null)
|
||||
for (File f : files) {
|
||||
if (!excludeFileFound)
|
||||
excludeFileFound = f.equals(exclude);
|
||||
if (!f.equals(exclude))
|
||||
deleteDirectory(f, exclude, ignoreLockedFiles);
|
||||
}
|
||||
}
|
||||
// Finally delete main file/dir if exclude file was not found in directory
|
||||
if (!excludeFileFound && !file.equals(exclude)) {
|
||||
try {
|
||||
deleteFileIfExists(file, ignoreLockedFiles);
|
||||
} catch (Throwable t) {
|
||||
log.error("Could not delete file. Error=" + t.toString());
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteFileIfExists(File file) throws IOException {
|
||||
deleteFileIfExists(file, true);
|
||||
}
|
||||
|
||||
public static void deleteFileIfExists(File file, boolean ignoreLockedFiles) throws IOException {
|
||||
try {
|
||||
if (Utilities.isWindows())
|
||||
file = file.getCanonicalFile();
|
||||
|
||||
if (file.exists() && !file.delete()) {
|
||||
if (ignoreLockedFiles) {
|
||||
// We check if file is locked. On Windows all open files are locked by the OS, so we
|
||||
if (isFileLocked(file))
|
||||
log.info("Failed to delete locked file: " + file.getAbsolutePath());
|
||||
} else {
|
||||
final String message = "Failed to delete file: " + file.getAbsolutePath();
|
||||
log.error(message);
|
||||
throw new IOException(message);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error(t.toString());
|
||||
t.printStackTrace();
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFileLocked(File file) {
|
||||
return !file.canWrite();
|
||||
}
|
||||
|
||||
public static void resourceToFile(String resourcePath, File destinationFile) throws ResourceNotFoundException, IOException {
|
||||
InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
|
||||
if (inputStream == null)
|
||||
throw new ResourceNotFoundException(resourcePath);
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(destinationFile)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
fileOutputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void renameFile(File oldFile, File newFile) throws IOException {
|
||||
if (Utilities.isWindows()) {
|
||||
// Work around an issue on Windows whereby you can't rename over existing files.
|
||||
final File canonical = newFile.getCanonicalFile();
|
||||
if (canonical.exists() && !canonical.delete()) {
|
||||
throw new IOException("Failed to delete canonical file for replacement with save");
|
||||
}
|
||||
if (!oldFile.renameTo(canonical)) {
|
||||
throw new IOException("Failed to rename " + oldFile + " to " + canonical);
|
||||
}
|
||||
} else if (!oldFile.renameTo(newFile)) {
|
||||
throw new IOException("Failed to rename " + oldFile + " to " + newFile);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyDirectory(File source, File destination) throws IOException {
|
||||
FileUtils.copyDirectory(source, destination);
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.storage;
|
||||
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class JsonFileManager {
|
||||
private final ThreadPoolExecutor executor = Utilities.getThreadPoolExecutor("saveToDiscExecutor", 5, 50, 60);
|
||||
private final File dir;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public JsonFileManager(File dir) {
|
||||
this.dir = dir;
|
||||
|
||||
if (!dir.exists())
|
||||
if (!dir.mkdir())
|
||||
log.warn("make dir failed");
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
UserThread.execute(JsonFileManager.this::shutDown);
|
||||
}, "WriteOnlyFileManager.ShutDownHook"));
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
executor.shutdown();
|
||||
try {
|
||||
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToDisc(String json, String fileName) {
|
||||
executor.execute(() -> {
|
||||
File jsonFile = new File(Paths.get(dir.getAbsolutePath(), fileName + ".json").toString());
|
||||
File tempFile = null;
|
||||
PrintWriter printWriter = null;
|
||||
try {
|
||||
tempFile = File.createTempFile("temp", null, dir);
|
||||
if (!executor.isShutdown() && !executor.isTerminated() && !executor.isTerminating())
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
printWriter = new PrintWriter(tempFile);
|
||||
printWriter.println(json);
|
||||
|
||||
FileUtil.renameFile(tempFile, jsonFile);
|
||||
} catch (Throwable t) {
|
||||
log.error("storageFile " + jsonFile.toString());
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
if (tempFile != null && tempFile.exists()) {
|
||||
log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + fileName);
|
||||
if (!tempFile.delete())
|
||||
log.error("Cannot delete temp file.");
|
||||
}
|
||||
|
||||
if (printWriter != null)
|
||||
printWriter.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Object readJsonFromDisc(String fileName) {
|
||||
final File jsonFile = new File(Paths.get(dir.getAbsolutePath(), fileName + ".json").toString());
|
||||
JSONParser parser = new JSONParser();
|
||||
try {
|
||||
return parser.parse(new FileReader(jsonFile));
|
||||
} catch (ParseException | IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.bisq.common.storage;
|
||||
|
||||
public class ResourceNotFoundException extends Exception {
|
||||
public ResourceNotFoundException(String path) {
|
||||
super("Resource not found: path = " + path);
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.storage;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* That class handles the storage of a particular object to disk using Protobuffer.
|
||||
* <p/>
|
||||
* For every data object we write a separate file to minimize the risk of corrupted files in case of inconsistency from newer versions.
|
||||
* In case of a corrupted file we backup the old file to a separate directory, so if it holds critical data it might be helpful for recovery.
|
||||
* <p/>
|
||||
* We also backup at first read the file, so we have a valid file form the latest version in case a write operation corrupted the file.
|
||||
* <p/>
|
||||
* The read operation is triggered just at object creation (startup) and is at the moment not executed on a background thread to avoid asynchronous behaviour.
|
||||
* As the data are small and it is just one read access the performance penalty is small and might be even worse to create and setup a thread for it.
|
||||
* <p/>
|
||||
* The write operation used a background thread and supports a delayed write to avoid too many repeated write operations.
|
||||
*/
|
||||
public class Storage<T extends PersistableEnvelope> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Storage.class);
|
||||
public static final String STORAGE_DIR = "storageDir";
|
||||
|
||||
private static DataBaseCorruptionHandler databaseCorruptionHandler;
|
||||
|
||||
public static void setDatabaseCorruptionHandler(DataBaseCorruptionHandler databaseCorruptionHandler) {
|
||||
Storage.databaseCorruptionHandler = databaseCorruptionHandler;
|
||||
}
|
||||
|
||||
public interface DataBaseCorruptionHandler {
|
||||
void onFileCorrupted(String fileName);
|
||||
}
|
||||
|
||||
private final File dir;
|
||||
private FileManager<T> fileManager;
|
||||
private File storageFile;
|
||||
private T persistable;
|
||||
private String fileName;
|
||||
private int numMaxBackupFiles = 10;
|
||||
private final PersistenceProtoResolver persistenceProtoResolver;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public Storage(@Named(STORAGE_DIR) File dir, PersistenceProtoResolver persistenceProtoResolver) {
|
||||
this.dir = dir;
|
||||
this.persistenceProtoResolver = persistenceProtoResolver;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T initAndGetPersistedWithFileName(String fileName, long delay) {
|
||||
this.fileName = fileName;
|
||||
storageFile = new File(dir, fileName);
|
||||
fileManager = new FileManager<>(dir, storageFile, delay, persistenceProtoResolver);
|
||||
return getPersisted();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T initAndGetPersisted(T persistable, long delay) {
|
||||
return initAndGetPersisted(persistable, persistable.getClass().getSimpleName(), delay);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T initAndGetPersisted(T persistable, String fileName, long delay) {
|
||||
this.persistable = persistable;
|
||||
this.fileName = fileName;
|
||||
storageFile = new File(dir, fileName);
|
||||
fileManager = new FileManager<>(dir, storageFile, delay, persistenceProtoResolver);
|
||||
return getPersisted();
|
||||
}
|
||||
|
||||
public void queueUpForSave() {
|
||||
queueUpForSave(persistable);
|
||||
}
|
||||
|
||||
public void queueUpForSave(long delayInMilli) {
|
||||
queueUpForSave(persistable, delayInMilli);
|
||||
}
|
||||
|
||||
public void setNumMaxBackupFiles(int numMaxBackupFiles) {
|
||||
this.numMaxBackupFiles = numMaxBackupFiles;
|
||||
}
|
||||
|
||||
// Save delayed and on a background thread
|
||||
public void queueUpForSave(T persistable) {
|
||||
if (persistable != null) {
|
||||
log.trace("save " + fileName);
|
||||
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
||||
|
||||
fileManager.saveLater(persistable);
|
||||
} else {
|
||||
log.trace("queueUpForSave called but no persistable set");
|
||||
}
|
||||
}
|
||||
|
||||
public void queueUpForSave(T persistable, long delayInMilli) {
|
||||
if (persistable != null) {
|
||||
log.trace("save " + fileName);
|
||||
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
||||
|
||||
fileManager.saveLater(persistable, delayInMilli);
|
||||
} else {
|
||||
log.trace("queueUpForSave called but no persistable set");
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(String fileName) {
|
||||
fileManager.removeFile(fileName);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// We do the file read on the UI thread to avoid problems from multi threading.
|
||||
// Data are small and read is done only at startup, so it is no performance issue.
|
||||
@Nullable
|
||||
private T getPersisted() {
|
||||
if (storageFile.exists()) {
|
||||
long now = System.currentTimeMillis();
|
||||
try {
|
||||
T persistedObject = fileManager.read(storageFile);
|
||||
log.trace("Read {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
|
||||
|
||||
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
||||
now = System.currentTimeMillis();
|
||||
fileManager.backupFile(fileName, numMaxBackupFiles);
|
||||
log.trace("Backup {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
|
||||
|
||||
return persistedObject;
|
||||
} catch (Throwable t) {
|
||||
log.error("We cannot read the persisted data. " +
|
||||
"We make a backup and remove the inconsistent file. fileName=" + fileName);
|
||||
log.error(t.getMessage());
|
||||
try {
|
||||
// We keep a backup which might be used for recovery
|
||||
fileManager.removeAndBackupFile(fileName);
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
log.error(e1.getMessage());
|
||||
// We swallow Exception if backup fails
|
||||
}
|
||||
if (databaseCorruptionHandler != null)
|
||||
databaseCorruptionHandler.onFileCorrupted(storageFile.getName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.taskrunner;
|
||||
|
||||
public class InterceptTaskException extends RuntimeException {
|
||||
public InterceptTaskException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.taskrunner;
|
||||
|
||||
public interface Model {
|
||||
void persist();
|
||||
|
||||
void onComplete();
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.taskrunner;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class Task<T extends Model> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Task.class);
|
||||
|
||||
public static Class<? extends Task> taskToIntercept;
|
||||
|
||||
private final TaskRunner taskHandler;
|
||||
protected final T model;
|
||||
protected String errorMessage = "An error occurred at task: " + getClass().getSimpleName();
|
||||
protected boolean completed;
|
||||
|
||||
public Task(TaskRunner taskHandler, T model) {
|
||||
this.taskHandler = taskHandler;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
abstract protected void run();
|
||||
|
||||
protected void runInterceptHook() {
|
||||
if (getClass() == taskToIntercept)
|
||||
throw new InterceptTaskException("Task intercepted for testing purpose. Task = " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
protected void appendToErrorMessage(String message) {
|
||||
errorMessage += "\n" + message;
|
||||
}
|
||||
|
||||
protected void appendExceptionToErrorMessage(Throwable t) {
|
||||
if (t.getMessage() != null)
|
||||
errorMessage += "\nException message: " + t.getMessage();
|
||||
else
|
||||
errorMessage += "\nException: " + t.toString();
|
||||
}
|
||||
|
||||
protected void complete() {
|
||||
completed = true;
|
||||
taskHandler.handleComplete();
|
||||
}
|
||||
|
||||
protected void failed(String message) {
|
||||
appendToErrorMessage(message);
|
||||
failed();
|
||||
}
|
||||
|
||||
protected void failed(Throwable t) {
|
||||
t.printStackTrace();
|
||||
appendExceptionToErrorMessage(t);
|
||||
failed();
|
||||
}
|
||||
|
||||
protected void failed() {
|
||||
log.error(errorMessage);
|
||||
taskHandler.handleErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.taskrunner;
|
||||
|
||||
import io.bisq.common.handlers.ErrorMessageHandler;
|
||||
import io.bisq.common.handlers.ResultHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
@Slf4j
|
||||
public class TaskRunner<T extends Model> {
|
||||
private final Queue<Class<? extends Task>> tasks = new LinkedBlockingQueue<>();
|
||||
private final T sharedModel;
|
||||
private final Class<T> sharedModelClass;
|
||||
private final ResultHandler resultHandler;
|
||||
private final ErrorMessageHandler errorMessageHandler;
|
||||
private boolean failed = false;
|
||||
private boolean isCanceled;
|
||||
|
||||
private Class<? extends Task> currentTask;
|
||||
|
||||
|
||||
public TaskRunner(T sharedModel, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
//noinspection unchecked
|
||||
this(sharedModel, (Class<T>) sharedModel.getClass(), resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public TaskRunner(T sharedModel, Class<T> sharedModelClass, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
this.sharedModel = sharedModel;
|
||||
this.resultHandler = resultHandler;
|
||||
this.errorMessageHandler = errorMessageHandler;
|
||||
this.sharedModelClass = sharedModelClass;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void addTasks(Class<? extends Task<T>>... items) {
|
||||
tasks.addAll(Arrays.asList(items));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
next();
|
||||
}
|
||||
|
||||
private void next() {
|
||||
if (!failed && !isCanceled) {
|
||||
if (tasks.size() > 0) {
|
||||
try {
|
||||
currentTask = tasks.poll();
|
||||
log.info("Run task: " + currentTask.getSimpleName());
|
||||
currentTask.getDeclaredConstructor(TaskRunner.class, sharedModelClass).newInstance(this, sharedModel).run();
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
handleErrorMessage("Error at taskRunner: " + throwable.getMessage());
|
||||
}
|
||||
} else {
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
}
|
||||
|
||||
void handleComplete() {
|
||||
log.trace("Task completed: " + currentTask.getSimpleName());
|
||||
sharedModel.persist();
|
||||
next();
|
||||
}
|
||||
|
||||
void handleErrorMessage(String errorMessage) {
|
||||
log.error("Task failed: " + currentTask.getSimpleName() + " / errorMessage: " + errorMessage);
|
||||
failed = true;
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
package io.bisq.common.util;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// Taken form https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform,
|
||||
// originally net.mightypork.rpack.utils.DesktopApi
|
||||
class DesktopUtil {
|
||||
|
||||
public static boolean browse(URI uri) {
|
||||
return openSystemSpecific(uri.toString()) || browseDESKTOP(uri);
|
||||
}
|
||||
|
||||
|
||||
public static boolean open(File file) {
|
||||
return openSystemSpecific(file.getPath()) || openDESKTOP(file);
|
||||
}
|
||||
|
||||
|
||||
public static boolean edit(File file) {
|
||||
// you can try something like
|
||||
// runCommand("gimp", "%s", file.getPath())
|
||||
// based on user preferences.
|
||||
return openSystemSpecific(file.getPath()) || editDESKTOP(file);
|
||||
}
|
||||
|
||||
|
||||
private static boolean openSystemSpecific(String what) {
|
||||
EnumOS os = getOs();
|
||||
if (os.isLinux()) {
|
||||
if (runCommand("kde-open", "%s", what)) return true;
|
||||
if (runCommand("gnome-open", "%s", what)) return true;
|
||||
if (runCommand("xdg-open", "%s", what)) return true;
|
||||
}
|
||||
|
||||
if (os.isMac()) {
|
||||
if (runCommand("open", "%s", what)) return true;
|
||||
}
|
||||
|
||||
if (os.isWindows()) {
|
||||
if (runCommand("explorer", "%s", what)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static boolean browseDESKTOP(URI uri) {
|
||||
|
||||
logOut("Trying to use Desktop.getDesktop().browse() with " + uri.toString());
|
||||
try {
|
||||
if (!Desktop.isDesktopSupported()) {
|
||||
logErr("Platform is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||
logErr("BROWSE is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Desktop.getDesktop().browse(uri);
|
||||
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
logErr("Error using desktop browse.", t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean openDESKTOP(File file) {
|
||||
|
||||
logOut("Trying to use Desktop.getDesktop().open() with " + file.toString());
|
||||
try {
|
||||
if (!Desktop.isDesktopSupported()) {
|
||||
logErr("Platform is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
|
||||
logErr("OPEN is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Desktop.getDesktop().open(file);
|
||||
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
logErr("Error using desktop open.", t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean editDESKTOP(File file) {
|
||||
|
||||
logOut("Trying to use Desktop.getDesktop().edit() with " + file);
|
||||
try {
|
||||
if (!Desktop.isDesktopSupported()) {
|
||||
logErr("Platform is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) {
|
||||
logErr("EDIT is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Desktop.getDesktop().edit(file);
|
||||
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
logErr("Error using desktop edit.", t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static boolean runCommand(String command, String args, String file) {
|
||||
|
||||
logOut("Trying to exec:\n cmd = " + command + "\n args = " + args + "\n %s = " + file);
|
||||
|
||||
String[] parts = prepareCommand(command, args, file);
|
||||
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(parts);
|
||||
if (p == null) return false;
|
||||
|
||||
try {
|
||||
int value = p.exitValue();
|
||||
if (value == 0) {
|
||||
logErr("Process ended immediately.");
|
||||
return false;
|
||||
} else {
|
||||
logErr("Process crashed.");
|
||||
return false;
|
||||
}
|
||||
} catch (IllegalThreadStateException e) {
|
||||
logErr("Process is running.");
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logErr("Error running command.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String[] prepareCommand(String command, String args, String file) {
|
||||
|
||||
List<String> parts = new ArrayList<>();
|
||||
parts.add(command);
|
||||
|
||||
if (args != null) {
|
||||
for (String s : args.split(" ")) {
|
||||
s = String.format(s, file); // put in the filename thing
|
||||
|
||||
parts.add(s.trim());
|
||||
}
|
||||
}
|
||||
|
||||
return parts.toArray(new String[parts.size()]);
|
||||
}
|
||||
|
||||
private static void logErr(String msg, Throwable t) {
|
||||
System.err.println(msg);
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
private static void logErr(String msg) {
|
||||
System.err.println(msg);
|
||||
}
|
||||
|
||||
private static void logOut(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
public enum EnumOS {
|
||||
linux,
|
||||
macos,
|
||||
solaris,
|
||||
unknown,
|
||||
windows;
|
||||
|
||||
public boolean isLinux() {
|
||||
|
||||
return this == linux || this == solaris;
|
||||
}
|
||||
|
||||
|
||||
public boolean isMac() {
|
||||
|
||||
return this == macos;
|
||||
}
|
||||
|
||||
|
||||
public boolean isWindows() {
|
||||
|
||||
return this == windows;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static EnumOS getOs() {
|
||||
|
||||
String s = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (s.contains("win")) {
|
||||
return EnumOS.windows;
|
||||
}
|
||||
|
||||
if (s.contains("mac")) {
|
||||
return EnumOS.macos;
|
||||
}
|
||||
|
||||
if (s.contains("solaris")) {
|
||||
return EnumOS.solaris;
|
||||
}
|
||||
|
||||
if (s.contains("sunos")) {
|
||||
return EnumOS.solaris;
|
||||
}
|
||||
|
||||
if (s.contains("linux")) {
|
||||
return EnumOS.linux;
|
||||
}
|
||||
|
||||
if (s.contains("unix")) {
|
||||
return EnumOS.linux;
|
||||
} else {
|
||||
return EnumOS.unknown;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package io.bisq.common.util;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class FunctionalReadWriteLock {
|
||||
@Getter
|
||||
private final Lock readLock;
|
||||
@Getter
|
||||
private final Lock writeLock;
|
||||
|
||||
public FunctionalReadWriteLock(boolean isFair) {
|
||||
this(new ReentrantReadWriteLock(isFair));
|
||||
}
|
||||
|
||||
private FunctionalReadWriteLock(ReadWriteLock lock) {
|
||||
readLock = lock.readLock();
|
||||
writeLock = lock.writeLock();
|
||||
}
|
||||
|
||||
public <T> T read(Supplier<T> block) {
|
||||
readLock.lock();
|
||||
try {
|
||||
return block.get();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void read(Runnable block) {
|
||||
readLock.lock();
|
||||
try {
|
||||
block.run();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T write(Supplier<T> block) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
return block.get();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(Runnable block) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
block.run();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void write2(Callable block) throws Exception {
|
||||
writeLock.lock();
|
||||
try {
|
||||
block.call();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface JsonExclude {
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package io.bisq.common.util;
|
||||
|
||||
import com.google.common.math.DoubleMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
public class MathUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(MathUtils.class);
|
||||
|
||||
public static double roundDouble(double value, int precision) {
|
||||
return roundDouble(value, precision, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static double roundDouble(double value, int precision, RoundingMode roundingMode) {
|
||||
if (precision < 0)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
try {
|
||||
BigDecimal bd = BigDecimal.valueOf(value);
|
||||
bd = bd.setScale(precision, roundingMode);
|
||||
return bd.doubleValue();
|
||||
} catch (Throwable t) {
|
||||
log.error(t.toString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static long roundDoubleToLong(double value) {
|
||||
return roundDoubleToLong(value, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static long roundDoubleToLong(double value, RoundingMode roundingMode) {
|
||||
return DoubleMath.roundToLong(value, roundingMode);
|
||||
}
|
||||
|
||||
public static int roundDoubleToInt(double value) {
|
||||
return roundDoubleToInt(value, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static int roundDoubleToInt(double value, RoundingMode roundingMode) {
|
||||
return DoubleMath.roundToInt(value, roundingMode);
|
||||
}
|
||||
|
||||
public static long doubleToLong(double value) {
|
||||
return new Double(value).longValue();
|
||||
}
|
||||
|
||||
public static double scaleUpByPowerOf10(double value, int exponent) {
|
||||
double factor = Math.pow(10, exponent);
|
||||
return value * factor;
|
||||
}
|
||||
|
||||
public static double scaleUpByPowerOf10(long value, int exponent) {
|
||||
double factor = Math.pow(10, exponent);
|
||||
return ((double) value) * factor;
|
||||
}
|
||||
|
||||
public static double scaleDownByPowerOf10(double value, int exponent) {
|
||||
double factor = Math.pow(10, exponent);
|
||||
return value / factor;
|
||||
}
|
||||
|
||||
public static double scaleDownByPowerOf10(long value, int exponent) {
|
||||
double factor = Math.pow(10, exponent);
|
||||
return ((double) value) / factor;
|
||||
}
|
||||
|
||||
public static double exactMultiply(double value1, double value2) {
|
||||
return BigDecimal.valueOf(value1).multiply(BigDecimal.valueOf(value2)).doubleValue();
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class Profiler {
|
||||
public static void printSystemLoad(Logger log) {
|
||||
log.info(printSystemLoadString());
|
||||
}
|
||||
|
||||
public static String printSystemLoadString() {
|
||||
return "System load: Memory (MB)): " + getUsedMemoryInMB() + " / No. of threads: " + Thread.activeCount();
|
||||
}
|
||||
|
||||
public static long getUsedMemoryInMB() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long free = runtime.freeMemory() / 1024 / 1024;
|
||||
long total = runtime.totalMemory() / 1024 / 1024;
|
||||
return total - free;
|
||||
}
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package io.bisq.common.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.List;
|
||||
|
||||
// Borrowed from: https://dzone.com/articles/programmatically-restart-java
|
||||
public class RestartUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(RestartUtil.class);
|
||||
|
||||
/**
|
||||
* Sun property pointing the main class and its arguments.
|
||||
* Might not be defined on non Hotspot VM implementations.
|
||||
*/
|
||||
public static final String SUN_JAVA_COMMAND = "sun.java.command";
|
||||
|
||||
public static void restartApplication(String logPath) throws IOException {
|
||||
try {
|
||||
String java = System.getProperty("java.home") + "/bin/java";
|
||||
List<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
|
||||
StringBuilder vmArgsOneLine = new StringBuilder();
|
||||
// if it's the agent argument : we ignore it otherwise the
|
||||
// address of the old application and the new one will be in conflict
|
||||
vmArguments.stream().filter(arg -> !arg.contains("-agentlib")).forEach(arg -> {
|
||||
vmArgsOneLine.append(arg);
|
||||
vmArgsOneLine.append(" ");
|
||||
});
|
||||
// init the command to execute, add the vm args
|
||||
final StringBuilder cmd = new StringBuilder(java + " " + vmArgsOneLine);
|
||||
|
||||
// program main and program arguments
|
||||
String[] mainCommand = System.getProperty(SUN_JAVA_COMMAND).split(" ");
|
||||
// program main is a jar
|
||||
if (mainCommand[0].endsWith(".jar")) {
|
||||
// if it's a jar, add -jar mainJar
|
||||
cmd.append("-jar ").append(new File(mainCommand[0]).getPath());
|
||||
} else {
|
||||
// else it's a .class, add the classpath and mainClass
|
||||
cmd.append("-cp \"").append(System.getProperty("java.class.path")).append("\" ").append(mainCommand[0]);
|
||||
}
|
||||
// finally add program arguments
|
||||
for (int i = 1; i < mainCommand.length; i++) {
|
||||
cmd.append(" ");
|
||||
cmd.append(mainCommand[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
final String command = "nohup " + cmd.toString() + " >/dev/null 2>" + logPath + " &";
|
||||
log.warn("\n\n############################################################\n" +
|
||||
"Executing cmd for restart: {}" +
|
||||
"\n############################################################\n\n",
|
||||
command);
|
||||
Runtime.getRuntime().exec(command);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Error while trying to restart the application", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Tuple2<A, B> implements Serializable {
|
||||
private static final long serialVersionUID = 1;
|
||||
|
||||
final public A first;
|
||||
final public B second;
|
||||
|
||||
public Tuple2(A first, B second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifiableIfStatement")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Tuple2)) return false;
|
||||
|
||||
Tuple2<?, ?> tuple2 = (Tuple2<?, ?>) o;
|
||||
|
||||
if (first != null ? !first.equals(tuple2.first) : tuple2.first != null) return false;
|
||||
return !(second != null ? !second.equals(tuple2.second) : tuple2.second != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = first != null ? first.hashCode() : 0;
|
||||
result = 31 * result + (second != null ? second.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
public class Tuple3<A, B, C> {
|
||||
final public A first;
|
||||
final public B second;
|
||||
final public C third;
|
||||
|
||||
public Tuple3(A first, B second, C third) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
this.third = third;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifiableIfStatement")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Tuple3)) return false;
|
||||
|
||||
Tuple3<?, ?, ?> tuple3 = (Tuple3<?, ?, ?>) o;
|
||||
|
||||
if (first != null ? !first.equals(tuple3.first) : tuple3.first != null) return false;
|
||||
if (second != null ? !second.equals(tuple3.second) : tuple3.second != null) return false;
|
||||
return !(third != null ? !third.equals(tuple3.third) : tuple3.third != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = first != null ? first.hashCode() : 0;
|
||||
result = 31 * result + (second != null ? second.hashCode() : 0);
|
||||
result = 31 * result + (third != null ? third.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
public class Tuple4<A, B, C, D> {
|
||||
final public A first;
|
||||
final public B second;
|
||||
final public C third;
|
||||
final public D forth;
|
||||
|
||||
public Tuple4(A first, B second, C third, D forth) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
this.third = third;
|
||||
this.forth = forth;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifiableIfStatement")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Tuple4)) return false;
|
||||
|
||||
Tuple4<?, ?, ?, ?> tuple4 = (Tuple4<?, ?, ?, ?>) o;
|
||||
|
||||
if (first != null ? !first.equals(tuple4.first) : tuple4.first != null) return false;
|
||||
if (second != null ? !second.equals(tuple4.second) : tuple4.second != null) return false;
|
||||
if (third != null ? !third.equals(tuple4.third) : tuple4.third != null) return false;
|
||||
return !(forth != null ? !forth.equals(tuple4.forth) : tuple4.forth != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = first != null ? first.hashCode() : 0;
|
||||
result = 31 * result + (second != null ? second.hashCode() : 0);
|
||||
result = 31 * result + (third != null ? third.hashCode() : 0);
|
||||
result = 31 * result + (forth != null ? forth.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,558 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.util;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.gson.*;
|
||||
import io.bisq.common.crypto.LimitedKeyStrengthException;
|
||||
import javafx.scene.input.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.awt.Desktop.*;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class Utilities {
|
||||
private static long lastTimeStamp = System.currentTimeMillis();
|
||||
public static final String LB = System.getProperty("line.separator");
|
||||
|
||||
// TODO check out Jackson lib
|
||||
public static String objectToJson(Object object) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.setExclusionStrategies(new AnnotationExclusionStrategy())
|
||||
/*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/
|
||||
/* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
return gson.toJson(object);
|
||||
}
|
||||
|
||||
public static ListeningExecutorService getListeningSingleThreadExecutor(String name) {
|
||||
return MoreExecutors.listeningDecorator(getSingleThreadExecutor(name));
|
||||
}
|
||||
|
||||
public static ExecutorService getSingleThreadExecutor(String name) {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat(name)
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
return Executors.newSingleThreadExecutor(threadFactory);
|
||||
}
|
||||
|
||||
public static ListeningExecutorService getListeningExecutorService(String name,
|
||||
int corePoolSize,
|
||||
int maximumPoolSize,
|
||||
long keepAliveTimeInSec) {
|
||||
return MoreExecutors.listeningDecorator(getThreadPoolExecutor(name, corePoolSize, maximumPoolSize, keepAliveTimeInSec));
|
||||
}
|
||||
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor(String name,
|
||||
int corePoolSize,
|
||||
int maximumPoolSize,
|
||||
long keepAliveTimeInSec) {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat(name)
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
|
||||
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
executor.setRejectedExecutionHandler((r, e) -> log.debug("RejectedExecutionHandler called"));
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String name,
|
||||
int corePoolSize,
|
||||
int maximumPoolSize,
|
||||
long keepAliveTimeInSec) {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat(name)
|
||||
.setDaemon(true)
|
||||
.setPriority(Thread.MIN_PRIORITY)
|
||||
.build();
|
||||
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
|
||||
executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS);
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
executor.setMaximumPoolSize(maximumPoolSize);
|
||||
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
executor.setRejectedExecutionHandler((r, e) -> log.debug("RejectedExecutionHandler called"));
|
||||
return executor;
|
||||
}
|
||||
|
||||
public static boolean isUnix() {
|
||||
return isOSX() || isLinux() || getOSName().contains("freebsd");
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return getOSName().contains("win");
|
||||
}
|
||||
|
||||
public static boolean isOSX() {
|
||||
return getOSName().contains("mac") || getOSName().contains("darwin");
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return getOSName().contains("linux");
|
||||
}
|
||||
|
||||
private static String getOSName() {
|
||||
return System.getProperty("os.name").toLowerCase(Locale.US);
|
||||
}
|
||||
|
||||
public static String getOSArchitecture() {
|
||||
String osArch = System.getProperty("os.arch");
|
||||
if (isWindows()) {
|
||||
// See: Like always windows needs extra treatment
|
||||
// https://stackoverflow.com/questions/20856694/how-to-find-the-os-bit-type
|
||||
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
|
||||
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
|
||||
return arch.endsWith("64")
|
||||
|| wow64Arch != null && wow64Arch.endsWith("64")
|
||||
? "64" : "32";
|
||||
} else if (osArch.contains("arm")) {
|
||||
// armv8 is 64 bit, armv7l is 32 bit
|
||||
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
|
||||
} else if (isLinux()) {
|
||||
return osArch.startsWith("i") ? "32" : "64";
|
||||
} else {
|
||||
return osArch.contains("64") ? "64" : osArch;
|
||||
}
|
||||
}
|
||||
|
||||
public static void printSysInfo() {
|
||||
log.info("System info: os.name={}; os.version={}; os.arch={}; sun.arch.data.model={}; JRE={}; JVM={}",
|
||||
System.getProperty("os.name"),
|
||||
System.getProperty("os.version"),
|
||||
System.getProperty("os.arch"),
|
||||
getJVMArchitecture(),
|
||||
(System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")"),
|
||||
(System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")")
|
||||
);
|
||||
}
|
||||
|
||||
public static String getJVMArchitecture() {
|
||||
return System.getProperty("sun.arch.data.model");
|
||||
}
|
||||
|
||||
public static boolean isCorrectOSArchitecture() {
|
||||
boolean result = getOSArchitecture().endsWith(getJVMArchitecture());
|
||||
if (!result) {
|
||||
log.warn("System.getProperty(\"os.arch\") " + System.getProperty("os.arch"));
|
||||
log.warn("System.getenv(\"ProgramFiles(x86)\") " + System.getenv("ProgramFiles(x86)"));
|
||||
log.warn("System.getenv(\"PROCESSOR_ARCHITECTURE\")" + System.getenv("PROCESSOR_ARCHITECTURE"));
|
||||
log.warn("System.getenv(\"PROCESSOR_ARCHITEW6432\") " + System.getenv("PROCESSOR_ARCHITEW6432"));
|
||||
log.warn("System.getProperty(\"sun.arch.data.model\") " + System.getProperty("sun.arch.data.model"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void openURI(URI uri) throws IOException {
|
||||
if (!isLinux()
|
||||
&& isDesktopSupported()
|
||||
&& getDesktop().isSupported(Action.BROWSE)) {
|
||||
getDesktop().browse(uri);
|
||||
} else {
|
||||
// Maybe Application.HostServices works in those cases?
|
||||
// HostServices hostServices = getHostServices();
|
||||
// hostServices.showDocument(uri.toString());
|
||||
|
||||
// On Linux Desktop is poorly implemented.
|
||||
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
||||
if (!DesktopUtil.browse(uri))
|
||||
throw new IOException("Failed to open URI: " + uri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void openFile(File file) throws IOException {
|
||||
if (!isLinux()
|
||||
&& isDesktopSupported()
|
||||
&& getDesktop().isSupported(Action.OPEN)) {
|
||||
getDesktop().open(file);
|
||||
} else {
|
||||
// Maybe Application.HostServices works in those cases?
|
||||
// HostServices hostServices = getHostServices();
|
||||
// hostServices.showDocument(uri.toString());
|
||||
|
||||
// On Linux Desktop is poorly implemented.
|
||||
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
||||
if (!DesktopUtil.open(file))
|
||||
throw new IOException("Failed to open file: " + file.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTmpDir() {
|
||||
return System.getProperty("java.io.tmpdir");
|
||||
}
|
||||
|
||||
public static String getDownloadOfHomeDir() {
|
||||
File file = new File(getSystemHomeDirectory() + "/Downloads");
|
||||
if (file.exists())
|
||||
return file.getAbsolutePath();
|
||||
else
|
||||
return getSystemHomeDirectory();
|
||||
}
|
||||
|
||||
public static void printSystemLoad() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long free = runtime.freeMemory() / 1024 / 1024;
|
||||
long total = runtime.totalMemory() / 1024 / 1024;
|
||||
long used = total - free;
|
||||
log.info("System load (no. threads/used memory (MB)): " + Thread.activeCount() + "/" + used);
|
||||
}
|
||||
|
||||
public static void copyToClipboard(String content) {
|
||||
try {
|
||||
if (content != null && content.length() > 0) {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent clipboardContent = new ClipboardContent();
|
||||
clipboardContent.putString(content);
|
||||
clipboard.setContent(clipboardContent);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error("copyToClipboard failed " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] concatByteArrays(byte[]... arrays) {
|
||||
int totalLength = 0;
|
||||
for (byte[] array : arrays) {
|
||||
totalLength += array.length;
|
||||
}
|
||||
|
||||
byte[] result = new byte[totalLength];
|
||||
int currentIndex = 0;
|
||||
for (byte[] array : arrays) {
|
||||
System.arraycopy(array, 0, result, currentIndex, array.length);
|
||||
currentIndex += array.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> T jsonToObject(String jsonString, Class<T> classOfT) {
|
||||
Gson gson =
|
||||
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
||||
return gson.fromJson(jsonString, classOfT);
|
||||
}
|
||||
|
||||
public static <T extends Serializable> T deserialize(byte[] data) {
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||
ObjectInput in = null;
|
||||
Object result = null;
|
||||
try {
|
||||
in = new ObjectInputStream(bis);
|
||||
result = in.readObject();
|
||||
if (!(result instanceof Serializable))
|
||||
throw new RuntimeException("Object not of type Serializable");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
bis.close();
|
||||
} catch (IOException ex) {
|
||||
// ignore close exception
|
||||
}
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// ignore close exception
|
||||
}
|
||||
}
|
||||
//noinspection unchecked,ConstantConditions
|
||||
return (T) result;
|
||||
}
|
||||
|
||||
public static byte[] serialize(Serializable object) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutput out = null;
|
||||
byte[] result = null;
|
||||
try {
|
||||
out = new ObjectOutputStream(bos);
|
||||
out.writeObject(object);
|
||||
out.flush();
|
||||
result = bos.toByteArray().clone();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends Serializable> T cloneObject(Serializable object) {
|
||||
return deserialize(serialize(object));
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static void printElapsedTime(String msg) {
|
||||
if (!msg.isEmpty()) {
|
||||
msg += " / ";
|
||||
}
|
||||
long timeStamp = System.currentTimeMillis();
|
||||
log.debug(msg + "Elapsed: " + String.valueOf(timeStamp - lastTimeStamp));
|
||||
lastTimeStamp = timeStamp;
|
||||
}
|
||||
|
||||
public static void printElapsedTime() {
|
||||
printElapsedTime("");
|
||||
}
|
||||
|
||||
public static void setThreadName(String name) {
|
||||
Thread.currentThread().setName(name + "-" + new Random().nextInt(10000));
|
||||
}
|
||||
|
||||
public static boolean isDirectory(String path) {
|
||||
return new File(path).isDirectory();
|
||||
}
|
||||
|
||||
public static String getSystemHomeDirectory() {
|
||||
return Utilities.isWindows() ? System.getenv("USERPROFILE") : System.getProperty("user.home");
|
||||
}
|
||||
|
||||
public static String encodeToHex(@Nullable byte[] bytes, boolean allowNullable) {
|
||||
if (allowNullable)
|
||||
return bytes != null ? Utils.HEX.encode(bytes) : "null";
|
||||
else
|
||||
return Utils.HEX.encode(checkNotNull(bytes, "bytes must not be null at encodeToHex"));
|
||||
}
|
||||
|
||||
public static String bytesAsHexString(@Nullable byte[] bytes) {
|
||||
return encodeToHex(bytes, true);
|
||||
}
|
||||
|
||||
public static String encodeToHex(@Nullable byte[] bytes) {
|
||||
return encodeToHex(bytes, false);
|
||||
}
|
||||
|
||||
public static byte[] decodeFromHex(String encoded) {
|
||||
return Utils.HEX.decode(encoded);
|
||||
}
|
||||
|
||||
public static boolean isAltOrCtrlPressed(KeyCode keyCode, KeyEvent keyEvent) {
|
||||
return isAltPressed(keyCode, keyEvent) || isCtrlPressed(keyCode, keyEvent);
|
||||
}
|
||||
|
||||
public static boolean isCtrlPressed(KeyCode keyCode, KeyEvent keyEvent) {
|
||||
return new KeyCodeCombination(keyCode, KeyCombination.SHORTCUT_DOWN).match(keyEvent) ||
|
||||
new KeyCodeCombination(keyCode, KeyCombination.CONTROL_DOWN).match(keyEvent);
|
||||
}
|
||||
|
||||
public static boolean isAltPressed(KeyCode keyCode, KeyEvent keyEvent) {
|
||||
return new KeyCodeCombination(keyCode, KeyCombination.ALT_DOWN).match(keyEvent);
|
||||
}
|
||||
|
||||
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2) {
|
||||
return ArrayUtils.addAll(array1, array2);
|
||||
}
|
||||
|
||||
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3) {
|
||||
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, array3));
|
||||
}
|
||||
|
||||
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3, byte[] array4) {
|
||||
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, ArrayUtils.addAll(array3, array4)));
|
||||
}
|
||||
|
||||
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3, byte[] array4, byte[] array5) {
|
||||
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, ArrayUtils.addAll(array3, ArrayUtils.addAll(array4, array5))));
|
||||
}
|
||||
|
||||
public static Date getUTCDate(int year, int month, int dayOfMonth) {
|
||||
GregorianCalendar calendar = new GregorianCalendar(year, month, dayOfMonth);
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stringList String of comma separated tokens.
|
||||
* @param allowWhitespace If white space inside the list tokens is allowed. If not the token will be ignored.
|
||||
* @return Set of tokens
|
||||
*/
|
||||
public static Set<String> commaSeparatedListToSet(String stringList, boolean allowWhitespace) {
|
||||
if (stringList != null) {
|
||||
return Splitter.on(",")
|
||||
.splitToList(allowWhitespace ? stringList : StringUtils.deleteWhitespace(stringList))
|
||||
.stream()
|
||||
.filter(e -> !e.isEmpty())
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(JsonExclude.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkCryptoPolicySetup() throws NoSuchAlgorithmException, LimitedKeyStrengthException {
|
||||
if (Cipher.getMaxAllowedKeyLength("AES") > 128)
|
||||
log.debug("Congratulations, you have unlimited key length support!");
|
||||
else
|
||||
throw new LimitedKeyStrengthException();
|
||||
}
|
||||
|
||||
public static String toTruncatedString(Object message, int maxLength) {
|
||||
if (message != null) {
|
||||
return StringUtils.abbreviate(message.toString(), maxLength).replace("\n", "");
|
||||
}
|
||||
return "null";
|
||||
}
|
||||
|
||||
public static String toTruncatedString(Object message) {
|
||||
return toTruncatedString(message, 200);
|
||||
}
|
||||
|
||||
public static String getRandomPrefix(int minLength, int maxLength) {
|
||||
int length = minLength + new Random().nextInt(maxLength - minLength + 1);
|
||||
String result;
|
||||
switch (new Random().nextInt(3)) {
|
||||
case 0:
|
||||
result = RandomStringUtils.randomAlphabetic(length);
|
||||
break;
|
||||
case 1:
|
||||
result = RandomStringUtils.randomNumeric(length);
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
result = RandomStringUtils.randomAlphanumeric(length);
|
||||
}
|
||||
|
||||
switch (new Random().nextInt(3)) {
|
||||
case 0:
|
||||
result = result.toUpperCase();
|
||||
break;
|
||||
case 1:
|
||||
result = result.toLowerCase();
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getShortId(String id) {
|
||||
return getShortId(id, "-");
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static String getShortId(String id, String sep) {
|
||||
String[] chunks = id.split(sep);
|
||||
if (chunks.length > 0)
|
||||
return chunks[0];
|
||||
else
|
||||
return id.substring(0, Math.min(8, id.length()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String collectionToCSV(Collection collection) {
|
||||
return collection.stream().map(Object::toString).collect(Collectors.joining(",")).toString();
|
||||
}
|
||||
|
||||
public static void removeCryptographyRestrictions() {
|
||||
if (!isRestrictedCryptography()) {
|
||||
System.out.println("Cryptography restrictions removal not needed");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
/*
|
||||
* Do the following, but with reflection to bypass access checks:
|
||||
*
|
||||
* JceSecurity.isRestricted = false;
|
||||
* JceSecurity.defaultPolicy.perms.clear();
|
||||
* JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
|
||||
*/
|
||||
final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
|
||||
final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
|
||||
final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
|
||||
|
||||
final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
|
||||
isRestrictedField.setAccessible(true);
|
||||
final Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
|
||||
isRestrictedField.set(null, false);
|
||||
|
||||
final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
|
||||
defaultPolicyField.setAccessible(true);
|
||||
final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
|
||||
|
||||
final Field perms = cryptoPermissions.getDeclaredField("perms");
|
||||
perms.setAccessible(true);
|
||||
((Map<?, ?>) perms.get(defaultPolicy)).clear();
|
||||
|
||||
final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
|
||||
instance.setAccessible(true);
|
||||
defaultPolicy.add((Permission) instance.get(null));
|
||||
|
||||
System.out.println("Successfully removed cryptography restrictions");
|
||||
} catch (final Exception e) {
|
||||
System.err.println("Failed to remove cryptography restrictions" + e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRestrictedCryptography() {
|
||||
// This matches Oracle Java 7 and 8, but not Java 9 or OpenJDK.
|
||||
final String name = System.getProperty("java.runtime.name");
|
||||
final String ver = System.getProperty("java.version");
|
||||
return name != null && name.equals("Java(TM) SE Runtime Environment")
|
||||
&& ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<!-- <logger name="io.bisq.core.storage.Storage" level="WARN"/>
|
||||
<logger name="io.bisq.core.storage.FileManager" level="WARN"/>
|
||||
<logger name="io.bisq.locale.BSResources" level="ERROR"/>-->
|
||||
|
||||
<!-- <logger name="io.bisq.p2p.peers.PeerGroup" level="TRACE"/>
|
||||
|
||||
|
||||
|
||||
<logger name="io.bisq.p2p.P2PService" level="TRACE"/>
|
||||
<logger name="io.bisq.p2p.storage.ProtectedExpirableDataStorage" level="TRACE"/>
|
||||
<logger name="io.bisq.p2p.network.LocalhostNetworkNode" level="TRACE"/>
|
||||
<logger name="io.bisq.p2p.network.TorNetworkNode" level="TRACE"/>
|
||||
<logger name="io.bisq.p2p.network.NetworkNode" level="TRACE"/>-->
|
||||
|
||||
|
||||
<!-- <logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/>
|
||||
|
||||
<logger name="io.bisq.btc.AddressBasedCoinSelector" level="WARN"/>
|
||||
<logger name="io.bisq.storage.Storage" level="WARN"/>
|
||||
|
||||
<logger name="io.bisq.gui.util.Profiler" level="ERROR"/>
|
||||
<logger name="io.bisq.temp.storage.RemoteStorage" level="WARN"/>
|
||||
<logger name="io.bisq.storage.FileManager" level="WARN"/>
|
||||
|
||||
<logger name="org.bitcoinj" level="WARN"/>
|
||||
|
||||
<logger name="org.bitcoinj.core.BitcoinSerializer" level="WARN"/>
|
||||
<logger name="org.bitcoinj.core.Peer" level="WARN"/>
|
||||
<logger name="org.bitcoinj.core.HeadersMessage" level="WARN"/>
|
||||
<logger name="org.bitcoinj.core.AbstractBlockChain" level="ERROR"/>-->
|
||||
|
||||
<!-- <logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||
<logger name="org.bitcoinj" level="WARN"/>-->
|
||||
|
||||
</configuration>
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.app;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CapabilitiesTest {
|
||||
|
||||
@Test
|
||||
public void testVersionNumber() {
|
||||
// if required are null or empty its true
|
||||
assertTrue(Capabilities.isCapabilitySupported(null, null));
|
||||
assertTrue(Capabilities.isCapabilitySupported(null, Arrays.asList()));
|
||||
assertTrue(Capabilities.isCapabilitySupported(null, Arrays.asList(0)));
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), null));
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), Arrays.asList()));
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), Arrays.asList(0)));
|
||||
|
||||
// required are not null and not empty but supported is null or empty its false
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), null));
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList()));
|
||||
|
||||
// single match
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(0)));
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(1), Arrays.asList(0)));
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(1)));
|
||||
|
||||
// multi match
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(0, 1)));
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(1, 2)));
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(0, 1)));
|
||||
assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(1,0)));
|
||||
assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(0)));
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.common.app;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class VersionTest {
|
||||
|
||||
@Test
|
||||
public void testVersionNumber() {
|
||||
assertEquals(0, Version.getMajorVersion("0.0.0"));
|
||||
assertEquals(1, Version.getMajorVersion("1.0.0"));
|
||||
|
||||
assertEquals(0, Version.getMinorVersion("0.0.0"));
|
||||
assertEquals(5, Version.getMinorVersion("0.5.0"));
|
||||
|
||||
assertEquals(0, Version.getPatchVersion("0.0.0"));
|
||||
assertEquals(5, Version.getPatchVersion("0.0.5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNewVersion() {
|
||||
assertFalse(Version.isNewVersion("0.0.0", "0.0.0"));
|
||||
assertTrue(Version.isNewVersion("0.1.0", "0.0.0"));
|
||||
assertTrue(Version.isNewVersion("0.0.1", "0.0.0"));
|
||||
assertTrue(Version.isNewVersion("1.0.0", "0.0.0"));
|
||||
assertTrue(Version.isNewVersion("0.5.1", "0.5.0"));
|
||||
assertFalse(Version.isNewVersion("0.5.0", "0.5.1"));
|
||||
assertTrue(Version.isNewVersion("0.6.0", "0.5.0"));
|
||||
assertTrue(Version.isNewVersion("0.6.0", "0.5.1"));
|
||||
assertFalse(Version.isNewVersion("0.5.0", "1.5.0"));
|
||||
assertFalse(Version.isNewVersion("0.4.9", "0.5.0"));
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package io.bisq.common.locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CurrencyUtilTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Locale.setDefault(new Locale("en", "US"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTradeCurrency() {
|
||||
Optional<TradeCurrency> euro = CurrencyUtil.getTradeCurrency("EUR");
|
||||
Optional<TradeCurrency> naira = CurrencyUtil.getTradeCurrency("NGN");
|
||||
Optional<TradeCurrency> fake = CurrencyUtil.getTradeCurrency("FAK");
|
||||
|
||||
|
||||
assertTrue(euro.isPresent());
|
||||
assertTrue(naira.isPresent());
|
||||
assertFalse("Fake currency shouldn't exist",fake.isPresent());
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user