package com.mygeopay.core.util;
import com.mygeopay.core.coins.CoinID;
import com.mygeopay.core.coins.CoinType;
import com.mygeopay.core.coins.Value;
import com.mygeopay.core.coins.ValueType;
import com.google.common.collect.ImmutableList;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Monetary;
import org.bitcoinj.core.VersionedChecksummedBytes;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
/**
* @author Andreas Schildbach
* @author John L. Jegutanis
*/
public class GenericUtils {
private static final Pattern charactersO0 = Pattern.compile("[0O]");
private static final Pattern characterIl = Pattern.compile("[Il]");
private static final Pattern notBase58 = Pattern.compile("[^123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]");
public static String fixAddress(final String input) {
String fixed = charactersO0.matcher(input).replaceAll("o");
fixed = characterIl.matcher(fixed).replaceAll("1");
fixed = notBase58.matcher(fixed).replaceAll("");
return fixed;
}
public static String addressSplitToGroupsMultiline(final String address) {
StringBuilder sb = new StringBuilder();
sb.append(address.substring(0, 4));
sb.append(" ");
sb.append(address.substring(4, 8));
sb.append(" ");
sb.append(address.substring(8, 12));
sb.append(" ");
sb.append(address.substring(12, 17));
sb.append("\n");
sb.append(address.substring(17, 21));
sb.append(" ");
sb.append(address.substring(21, 25));
sb.append(" ");
sb.append(address.substring(25, 29));
sb.append(" ");
sb.append(address.substring(29));
return sb.toString();
}
public static String addressSplitToGroups(final Address address) {
return addressSplitToGroups(address.toString());
}
public static String addressSplitToGroups(final String address) {
StringBuilder sb = new StringBuilder();
sb.append(address.substring(0, 5));
sb.append(" ");
sb.append(address.substring(5, 9));
sb.append(" ");
sb.append(address.substring(9, 13));
sb.append(" ");
sb.append(address.substring(13, 17));
sb.append(" ");
sb.append(address.substring(17, 21));
sb.append(" ");
sb.append(address.substring(21, 25));
sb.append(" ");
sb.append(address.substring(25, 29));
sb.append(" ");
sb.append(address.substring(29));
return sb.toString();
}
public static String formatValue(@Nonnull final Value value) {
return formatCoinValue(value.type, value.toCoin(), "", "-", 8, 0);
}
public static String formatCoinValue(@Nonnull final ValueType type, @Nonnull final Monetary value) {
return formatCoinValue(type, value, "", "-", 8, 0);
}
public static String formatCoinValue(@Nonnull final ValueType type, @Nonnull final Monetary value,
final int precision, final int shift) {
return formatCoinValue(type, value, "", "-", precision, shift);
}
public static String formatCoinValue(@Nonnull final ValueType type, @Nonnull final Monetary value,
@Nonnull final String plusSign, @Nonnull final String minusSign,
final int precision, final int shift) {
return formatValue(type.getUnitExponent(), value, plusSign, minusSign, precision, shift, false);
}
public static String formatCoinValue(@Nonnull final ValueType type, @Nonnull final Monetary value,
boolean removeFinalZeroes) {
return formatValue(type.getUnitExponent(), value, "", "-", 8, 0, removeFinalZeroes);
}
private static String formatValue(final long unitExponent, @Nonnull final Monetary value,
@Nonnull final String plusSign, @Nonnull final String minusSign,
final int precision, final int shift, boolean removeFinalZeroes) {
long longValue = value.getValue();
final String sign = value.signum() == -1 ? minusSign : plusSign;
String formatedValue;
if (shift == 0) {
long units = Math.round(Math.pow(10, unitExponent));
long precisionUnits = (long) (units / Math.pow(10, precision));
long roundingPrecisionUnits = precisionUnits / 2;
if (precision == 2 || precision == 4 || precision == 6 || precision == 8) {
if (roundingPrecisionUnits > 0) {
longValue = longValue - longValue % precisionUnits + longValue % precisionUnits / roundingPrecisionUnits * precisionUnits;
}
} else {
throw new IllegalArgumentException("cannot handle precision/shift: " + precision + "/" + shift);
}
final long absValue = Math.abs(longValue);
final long coins = absValue / units;
final int satoshis = (int) (absValue % units);
if (isShiftPossible(units, satoshis, 100)) {
formatedValue = String.format(Locale.US, "%d.%02d",
coins, getShiftedCents(units, satoshis, 100));
} else if (isShiftPossible(units, satoshis, 10000)) {
formatedValue = String.format(Locale.US, "%d.%04d",
coins, getShiftedCents(units, satoshis, 10000));
} else if (isShiftPossible(units, satoshis, 1000000)) {
formatedValue = String.format(Locale.US, "%d.%06d", coins,
getShiftedCents(units, satoshis, 1000000));
} else {
formatedValue = String.format(Locale.US, "%d.%08d", coins, satoshis);
}
} else {
throw new IllegalArgumentException("cannot handle shift: " + shift);
}
// Relax precision if incorrectly shows value as 0.00 but is in reality not zero
if (formatedValue.equals("0.00") && value.getValue() != 0) {
return formatValue(unitExponent, value, plusSign, minusSign, precision + 2, shift, removeFinalZeroes);
}
// Remove final zeroes if requested
while (removeFinalZeroes && !formatedValue.isEmpty() &&
formatedValue.contains(".") && formatedValue.endsWith("0")) {
formatedValue = formatedValue.substring(0, formatedValue.length() - 1);
}
if (removeFinalZeroes && !formatedValue.isEmpty() && formatedValue.endsWith(".")) {
formatedValue = formatedValue.substring(0, formatedValue.length() - 1);
}
// Add the sign if needed
formatedValue = String.format(Locale.US, "%s%s", sign, formatedValue);
return formatedValue;
}
private static long getShiftedCents(long units, int satoshis, int centAmount) {
return satoshis / (units / centAmount);
}
private static boolean isShiftPossible(long units, int satoshis, int centAmount) {
return units / centAmount != 0 && satoshis % (units / centAmount) == 0;
}
public static String formatFiatValue(final Value fiat, final int precision, final int shift) {
return formatValue(fiat.smallestUnitExponent(), fiat, "", "-", precision, shift, false);
}
public static String formatFiatValue(Value fiat) {
return formatFiatValue(fiat, 2, 0);
}
/**
* Parses the provided string and returns the possible supported coin types.
* Throws an AddressFormatException if the string is not a valid address or not supported.
*/
public static List<CoinType> getPossibleTypes(String addressStr) throws AddressFormatException {
VersionedChecksummedBytes parsed = new VersionedChecksummedBytes(addressStr) { };
ImmutableList.Builder<CoinType> builder = ImmutableList.builder();
int version = parsed.getVersion();
for (CoinType type : CoinID.getSupportedCoins()) {
for (int addressCode : type.getAcceptableAddressCodes()) {
if (addressCode == version) {
builder.add(type);
break;
}
}
}
List<CoinType> possibleTypes = builder.build();
if (possibleTypes.isEmpty()) {
throw new AddressFormatException("Unsupported address: " + addressStr);
}
return builder.build();
}
public static List<CoinType> getPossibleTypes(Address address) throws AddressFormatException {
return getPossibleTypes(address.toString());
}
public static boolean hasMultipleTypes(Address address) {
return hasMultipleTypes(address.toString());
}
public static boolean hasMultipleTypes(String addressStr) {
try {
return getPossibleTypes(addressStr).size() > 1;
} catch (AddressFormatException e) {
return false;
}
}
}