From c9e29199a6a7b2a7885f8c29db66eab3fb51d9fe Mon Sep 17 00:00:00 2001 From: Sandro Machado Date: Wed, 4 May 2016 23:40:05 +0100 Subject: [PATCH] Fix parse for URI with parameters with .*: --- .../bitcoinpaymenturi/BitcoinPaymentURI.java | 222 +++++++++--------- .../test/BitcoinPaymentURITest.java | 47 ++-- 2 files changed, 135 insertions(+), 134 deletions(-) diff --git a/src/main/java/com/sandro/bitcoinpaymenturi/BitcoinPaymentURI.java b/src/main/java/com/sandro/bitcoinpaymenturi/BitcoinPaymentURI.java index c268a96..a4860d6 100644 --- a/src/main/java/com/sandro/bitcoinpaymenturi/BitcoinPaymentURI.java +++ b/src/main/java/com/sandro/bitcoinpaymenturi/BitcoinPaymentURI.java @@ -12,117 +12,117 @@ /** * Java library to handle Bitcoin payment URI. - * This library is based on the specification at the BIP 21. - * + * This library is based on the specification at the BIP 21. + * * The BIT is available at: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki */ public class BitcoinPaymentURI { - + private static final String SCHEME = "bitcoin:"; private static final String PARAMETER_AMOUNT = "amount"; private static final String PARAMETER_LABEL = "label"; private static final String PARAMETER_MESSAGE = "message"; - + private final String address; private final HashMap parameters; - + private BitcoinPaymentURI(Builder builder) { this.address = builder.address; - + parameters = new HashMap(); - + if (builder.amount != null) { parameters.put(PARAMETER_AMOUNT, new Parameter(String.valueOf(builder.amount), false)); } - + if (builder.label != null) { parameters.put(PARAMETER_LABEL, new Parameter(builder.label, false)); } - + if (builder.message != null) { parameters.put(PARAMETER_MESSAGE, new Parameter(builder.message, false)); } - + if (builder.otherParameters != null) { parameters.putAll(builder.otherParameters); } } - + /** * Gets the URI Bitcoin address. - * + * * @return the URI Bitcoin address. */ - + public String getAddress() { return address; } /** * Gets the URI amount. - * + * * @return the URI amount. */ - + public Double getAmount() { if (parameters.get(PARAMETER_AMOUNT) == null) { return null; } - + return Double.valueOf(parameters.get(PARAMETER_AMOUNT).getValue()); } - + /** * Gets the URI label. - * + * * @return the URI label. */ - + public String getLabel() { if (parameters.get(PARAMETER_LABEL) == null) { return null; } - + return parameters.get(PARAMETER_LABEL).getValue(); } - + /** * Gets the URI message. - * + * * @return the URI message. */ - + public String getMessage() { if (parameters.get(PARAMETER_MESSAGE) == null) { return null; } - + return parameters.get(PARAMETER_MESSAGE).getValue(); } - + /** * Gets the URI parameters. - * + * * @return the URI parameters. */ - + public HashMap getParameters() { HashMap filteredParameters = new HashMap(parameters); - + filteredParameters.remove(PARAMETER_AMOUNT); filteredParameters.remove(PARAMETER_LABEL); filteredParameters.remove(PARAMETER_MESSAGE); - + return filteredParameters; } - + /** * Gets the URI. - * + * * @return a string with the URI. This string can be used to make a Bitcoin payment. */ - + public String getURI() { String queryParameters = null; try { @@ -132,52 +132,52 @@ public String getURI() { queryParameters = String.format("req-%s=%s", URLEncoder.encode(entry.getKey(), "UTF-8").replace("+", "%20"), URLEncoder.encode(entry.getValue().getValue(), "UTF-8").replace("+", "%20")); continue; - } - + } + queryParameters = String.format("%s=%s", URLEncoder.encode(entry.getKey(), "UTF-8").replace("+", "%20"), URLEncoder.encode(entry.getValue().getValue(), "UTF-8").replace("+", "%20")); - + continue; } - + if (entry.getValue().isRequired()) { queryParameters = String.format("%s&req-%s=%s", queryParameters, URLEncoder.encode(entry.getKey(), "UTF-8").replace("+", "%20"), URLEncoder.encode(entry.getValue().getValue(), "UTF-8").replace("+", "%20")); - + continue; } - + queryParameters = String.format("%s&%s=%s", queryParameters, URLEncoder.encode(entry.getKey(), "UTF-8").replace("+", "%20"), URLEncoder.encode(entry.getValue().getValue(), "UTF-8").replace("+", "%20")); } - + } catch (UnsupportedEncodingException e) { e.printStackTrace(); - + return null; } - + return String.format("%s%s%s", SCHEME, getAddress(), queryParameters == null ? "" : String.format("?%s", queryParameters)); } - + /** * Parses a string to a Bitcoin payment URI. - * + * * @param string The string to be parsed. - * + * * @return a Bitcoin payment URI if the URI is valid, or null for an invalid string. */ - + public static BitcoinPaymentURI parse(String string) { try { string = URLDecoder.decode(string, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); - + return null; } - + if (string == null) { return null; } - + if (string.isEmpty()) { return null; } @@ -185,194 +185,194 @@ public static BitcoinPaymentURI parse(String string) { if (!string.toLowerCase().startsWith(SCHEME)) { return null; } - - String bitcoinPaymentURIWithoutScheme = string.replaceFirst(".*:", ""); + + String bitcoinPaymentURIWithoutScheme = string.replaceFirst(SCHEME, ""); ArrayList bitcoinPaymentURIElements = new ArrayList<>(Arrays.asList(bitcoinPaymentURIWithoutScheme.split("\\?"))); - + if (bitcoinPaymentURIElements.size() != 1 && bitcoinPaymentURIElements.size() != 2) { return null; } - + if (bitcoinPaymentURIElements.get(0).length() == 0) { return null; } - + if (bitcoinPaymentURIElements.size() == 1) { return new Builder().address(bitcoinPaymentURIElements.get(0)).build(); } - + List queryParametersList = Arrays.asList(bitcoinPaymentURIElements.get(1).split("&")); - + if (queryParametersList.isEmpty()) { return new Builder().address(bitcoinPaymentURIElements.get(0)).build(); } - - HashMap queryParametersFiltered = new HashMap(); - + + HashMap queryParametersFiltered = new HashMap(); + for (String query : queryParametersList) { String[] queryParameter = query.split("="); - + try { queryParametersFiltered.put(queryParameter[0], queryParameter[1]); }catch(ArrayIndexOutOfBoundsException exception) { exception.printStackTrace(); - + return null; } } - + Builder bitcoinPaymentURIBuilder = new Builder().address(bitcoinPaymentURIElements.get(0)); - + if (queryParametersFiltered.containsKey(PARAMETER_AMOUNT)) { bitcoinPaymentURIBuilder.amount(Double.valueOf(queryParametersFiltered.get(PARAMETER_AMOUNT))); - + queryParametersFiltered.remove(PARAMETER_AMOUNT); } - + if (queryParametersFiltered.containsKey(PARAMETER_LABEL)) { bitcoinPaymentURIBuilder.label(queryParametersFiltered.get(PARAMETER_LABEL)); - + queryParametersFiltered.remove(PARAMETER_LABEL); } - + if (queryParametersFiltered.containsKey(PARAMETER_MESSAGE)) { bitcoinPaymentURIBuilder.message(queryParametersFiltered.get(PARAMETER_MESSAGE)); - + queryParametersFiltered.remove(PARAMETER_MESSAGE); } - + for (Map.Entry entry : queryParametersFiltered.entrySet()) { bitcoinPaymentURIBuilder.parameter(entry.getKey(), entry.getValue()); } - + return bitcoinPaymentURIBuilder.build(); } - + public static class Builder{ - + private String address; private Double amount; private String label; private String message; private HashMap otherParameters; - + /** * Returns a builder for the Bitcoin payment URI. */ public Builder() { } - + /** * Adds the address to the builder. - * + * * @param address The address. - * + * * @return the builder with the address. */ - + public Builder address(String address) { this.address = address; - + return this; } - + /** * Adds the amount to the builder. - * + * * @param amount The amount. - * + * * @return the builder with the amount. */ - + public Builder amount(Double amount) { this.amount = amount; - + return this; } - + /** * Adds the label to the builder. - * + * * @param label The label. - * + * * @return the builder with the label. */ - + public Builder label(String label) { this.label = label; - + return this; } - + /** * Adds the message to the builder. - * + * * @param message The message. - * + * * @return the builder with the message. */ - + public Builder message(String message) { this.message = message; - + return this; } - + /** * Adds a parameter to the builder. - * + * * @param key The parameter. * @param value The value. - * + * * @return the builder with the parameter. */ - + public Builder parameter(String key, String value) { if (otherParameters == null) { otherParameters = new HashMap(); } - + if (key.startsWith("req-")) { otherParameters.put(key.replace("req-", ""), new Parameter(value, true)); - + return this; } - + otherParameters.put(key, new Parameter(value, false)); - + return this; } - + /** * Adds a required to the builder. - * + * * @param key The key. * @param value The value. - * + * * @return the builder with the parameter. */ - + public Builder requiredParameter(String key, String value) { if (otherParameters == null) { otherParameters = new HashMap(); } - + otherParameters.put(key, new Parameter(value, true)); - + return this; } - + /** * Builds a Bitcoin payment URI. - * + * * @return a Bitcoin payment URI. */ - + public BitcoinPaymentURI build() { return new BitcoinPaymentURI(this); } - + } - + } diff --git a/src/test/java/com/sandro/bitcoinpaymenturi/test/BitcoinPaymentURITest.java b/src/test/java/com/sandro/bitcoinpaymenturi/test/BitcoinPaymentURITest.java index 893bec0..a67510c 100644 --- a/src/test/java/com/sandro/bitcoinpaymenturi/test/BitcoinPaymentURITest.java +++ b/src/test/java/com/sandro/bitcoinpaymenturi/test/BitcoinPaymentURITest.java @@ -6,67 +6,68 @@ import static org.junit.Assert.*; public class BitcoinPaymentURITest { - - @Test + + @Test public void testParseForAddressMethod() { BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); - + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertNull(bitcoinPaymentURI.getAmount()); assertNull(bitcoinPaymentURI.getLabel()); assertNull(bitcoinPaymentURI.getMessage()); assertEquals(bitcoinPaymentURI.getParameters().size(), 0); } - - @Test + + @Test public void testParseForAddressWithNameMethod() { BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Luke-Jr"); - + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertNull(bitcoinPaymentURI.getAmount()); assertEquals(bitcoinPaymentURI.getLabel(), "Luke-Jr"); assertNull(bitcoinPaymentURI.getMessage()); assertEquals(bitcoinPaymentURI.getParameters().size(), 0); } - - @Test + + @Test public void testParseForAddressWithAmountAndNameMethod() { BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=20.3&label=Luke-Jr"); - + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertEquals(bitcoinPaymentURI.getAmount(), 20,3); assertEquals(bitcoinPaymentURI.getLabel(), "Luke-Jr"); assertNull(bitcoinPaymentURI.getMessage()); assertEquals(bitcoinPaymentURI.getParameters().size(), 0); } - - @Test + + @Test public void testParseForAddressWithAmountAndNameAndMessageAndRequiredParameterMethod() { BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz"); - + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertEquals(bitcoinPaymentURI.getAmount(), Double.valueOf(50)); assertEquals(bitcoinPaymentURI.getLabel(), "Luke-Jr"); assertEquals(bitcoinPaymentURI.getMessage(), "Donation for project xyz"); assertEquals(bitcoinPaymentURI.getParameters().size(), 0); } - - @Test + + @Test public void testParseForAddressWithAmountAndNameAndMessageAndParametersMethod() { - BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?somethingyoudontunderstand=50&somethingelseyoudontget=999&req-app=appname"); - + BitcoinPaymentURI bitcoinPaymentURI = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?somethingyoudontunderstand=50&somethingelseyoudontget=999&r=https%3A%2F%2Ffoo.com%2Fi%2F7BpFbVsnh5PUisfh&req-app=appname"); + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertNull(bitcoinPaymentURI.getAmount()); assertNull(bitcoinPaymentURI.getLabel()); assertNull(bitcoinPaymentURI.getMessage()); - assertEquals(bitcoinPaymentURI.getParameters().size(), 3); + assertEquals(bitcoinPaymentURI.getParameters().size(), 4); assertEquals(bitcoinPaymentURI.getParameters().get("somethingyoudontunderstand").getValue(), "50"); assertEquals(bitcoinPaymentURI.getParameters().get("somethingelseyoudontget").getValue(), "999"); + assertEquals(bitcoinPaymentURI.getParameters().get("r").getValue(), "https://foo.com/i/7BpFbVsnh5PUisfh"); assertEquals(bitcoinPaymentURI.getParameters().get("app").getValue(), "appname"); assertTrue(bitcoinPaymentURI.getParameters().get("app").isRequired()); } - - @Test + + @Test public void testParseForInvalidAddressesMethod() { BitcoinPaymentURI bitcoinPaymentURI1 = BitcoinPaymentURI.parse("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?somethingyoudontunderstand=50&somethingelseyoudontget"); BitcoinPaymentURI bitcoinPaymentURI2 = BitcoinPaymentURI.parse("bitcoinX:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?somethingyoudontunderstand=50"); @@ -80,8 +81,8 @@ public void testParseForInvalidAddressesMethod() { assertNull(bitcoinPaymentURI4); assertNull(bitcoinPaymentURI5); } - - @Test + + @Test public void testBuilder() { BitcoinPaymentURI bitcoinPaymentURI = new BitcoinPaymentURI.Builder() .address("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W") @@ -91,7 +92,7 @@ public void testBuilder() { .parameter("foo", "bar") .requiredParameter("fiz", "biz") .build(); - + assertEquals(bitcoinPaymentURI.getAddress(), "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"); assertEquals(bitcoinPaymentURI.getAmount(), Double.valueOf(50)); assertEquals(bitcoinPaymentURI.getLabel(), "Luke-Jr"); @@ -103,5 +104,5 @@ public void testBuilder() { assertTrue(bitcoinPaymentURI.getParameters().get("fiz").isRequired()); assertEquals(bitcoinPaymentURI.getURI(), "bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Donation%20for%20project%20xyz&amount=50.0&req-fiz=biz&foo=bar&label=Luke-Jr"); } - + }