From 625c211b626e53920c8b8263f390f1d213b5b1a6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 17 Mar 2023 05:56:38 -0700 Subject: [PATCH] Added formatCreditCard --- .github/workflows/publish.yml | 1 + .idea/misc.xml | 3 ++ README.md | 30 ++++++----- lib/build.gradle.kts | 4 +- .../java/rife/render/FormatCreditCard.java | 44 +++++++++++++++ .../main/java/rife/render/RenderUtils.java | 54 ++++++++++++++++--- lib/src/main/java/rife/render/ShortenUrl.java | 3 +- lib/src/test/java/rife/render/TestEncode.java | 12 ----- lib/src/test/java/rife/render/TestFormat.java | 50 +++++++++++++++++ .../resources/templates/formatCreditCard.txt | 1 + 10 files changed, 168 insertions(+), 34 deletions(-) create mode 100644 lib/src/main/java/rife/render/FormatCreditCard.java create mode 100644 lib/src/test/java/rife/render/TestFormat.java create mode 100644 lib/src/test/resources/templates/formatCreditCard.txt diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9c75e12..e84f407 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,6 +1,7 @@ name: Publish to the Maven Central on: + workflow_dispatch: release: types: [released] diff --git a/.idea/misc.xml b/.idea/misc.xml index 2d17eca..0ccb376 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -44,6 +44,9 @@ + + + diff --git a/README.md b/README.md index 7cd7327..f8b68da 100644 --- a/README.md +++ b/README.md @@ -21,18 +21,24 @@ This project provides a collection of template renderers. ## Encoding Renderers -| Renderer | Description | -|:---------------------------------|:--------------------------------------------------------| -| `rife.render.EncodeBase64` | Encodes a template value to Base64 | -| `rife.render.EncodeHtml` | Encodes a template value to HTML | -| `rife.render.EncodeHtmlEntities` | Encodes a template value to HTML decimal entities | -| `rife.render.EncodeJs` | Encodes a template value to JavaScript/ECMAScript | -| `rife.render.EncodeJson` | Encodes a template value to JSON | -| `rife.render.EncodeQp` | Converts a template value to a quoted-printable string | -| `rife.render.EncodeUnicode` | Encodes a template value to Unicode escape codes | -| `rife.render.EncodeUrl` | URL-encodes a template value | -| `rife.render.EncodeXml` | Encodes a template value to XML | -| `rife.render.ShorteUrl` | Shortens a template value using [is.gd](https://is.gd/) | +| Renderer | Description | +|:---------------------------------|:-------------------------------------------------------| +| `rife.render.EncodeBase64` | Encodes a template value to Base64 | +| `rife.render.EncodeHtml` | Encodes a template value to HTML | +| `rife.render.EncodeHtmlEntities` | Encodes a template value to HTML decimal entities | +| `rife.render.EncodeJs` | Encodes a template value to JavaScript/ECMAScript | +| `rife.render.EncodeJson` | Encodes a template value to JSON | +| `rife.render.EncodeQp` | Converts a template value to a quoted-printable string | +| `rife.render.EncodeUnicode` | Encodes a template value to Unicode escape codes | +| `rife.render.EncodeUrl` | URL-encodes a template value | +| `rife.render.EncodeXml` | Encodes a template value to XML | + +## Format Renderers + +| Renderer | Description | +|:---------------------------------|:-----------------------------------------------------------------| +| `rife.render.formatCreditcard` | Formats a template credit card number value to the last 4 digits | +| `rife.render.ShorteUrl` | Shortens a template value using [is./gd](https://is.gd/) | ## Text Renderers diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 77c19a3..ca883c0 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -67,13 +67,13 @@ tasks { } } - javadoc { + javadoc { title = "RIFE2 Template Renderers" options { this as StandardJavadocDocletOptions keyWords(true) splitIndex(true) - links() + links("https://rife2.github.io/rife2/") } } } diff --git a/lib/src/main/java/rife/render/FormatCreditCard.java b/lib/src/main/java/rife/render/FormatCreditCard.java new file mode 100644 index 0000000..c7f6f89 --- /dev/null +++ b/lib/src/main/java/rife/render/FormatCreditCard.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package rife.render; + +import rife.template.Template; +import rife.template.ValueRenderer; + +/** + * Formats a template credit card number value to the last 4 digits. + * + *

Usage:

+ * + *
+ *   <!--v render:rife.render.FormatCreditCard:valueId/-->
+ *   {{v render:rife.render.FormatCreditCard:valueId/}}
+ * 
+ * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class FormatCreditCard implements ValueRenderer { + /** + * {@inheritDoc} + */ + @Override + public String render(Template template, String valueId, String differentiator) { + return RenderUtils.formatCreditCard(RenderUtils.fetchValue(template, differentiator)); + } +} diff --git a/lib/src/main/java/rife/render/RenderUtils.java b/lib/src/main/java/rife/render/RenderUtils.java index d722d93..ffd0dd2 100644 --- a/lib/src/main/java/rife/render/RenderUtils.java +++ b/lib/src/main/java/rife/render/RenderUtils.java @@ -53,7 +53,7 @@ public final class RenderUtils { /** * Encodes a string to JavaScript/ECMAScript. * - * @param src the source string. + * @param src the source string * @return the enocded string */ public static String encodeJS(String src) { @@ -96,11 +96,53 @@ public final class RenderUtils { return Convert.toString(value); } + /** + * Returns the last 4 digits a credit card number. The number must satisfy the Luhn algorithm. + * Non-digits are stripped from the number. + * + * @param src the credit card number + * @return the last 4 digits of the credit card number or empty + */ + public static String formatCreditCard(String src) { + if (src == null || src.isBlank()) { + return src; + } + + try { + var cc = src.replaceAll("[^0-9]", ""); + + var len = cc.length(); + if (len >= 4) { + // Luhn algorithm + var sum = 0; + boolean isSecond = false; + int digit; + for (int i = len - 1; i >= 0; i--) { + digit = cc.charAt(i) - '0'; + if (isSecond) { + digit = digit * 2; + } + sum += digit / 10; + sum += digit % 10; + + isSecond = !isSecond; + } + if (sum % 10 == 0) { + return cc.substring(len - 4); + } + } + } catch (NumberFormatException ignore) { + // do nothing + } + + return ""; + } + /** * Translates a String to/from ROT13. * - * @param src the source String. - * @return the translated String. + * @param src the source String + * @return the translated String */ public static String rot13(String src) { if (src == null || src.isBlank()) { @@ -144,7 +186,7 @@ public final class RenderUtils { * @return the short URL */ public static String shortenUrl(String url) { - if (url == null || url.isBlank() || !url.matches("^[Hh][Tt][Tt][Pp][Ss]?:\\/\\/\\w.*")) { + if (url == null || url.isBlank() || !url.matches("^[Hh][Tt][Tt][Pp][Ss]?://\\w.*")) { return url; } @@ -203,8 +245,8 @@ public final class RenderUtils { /** * Converts a text string to HTML decimal entities. * - * @param src the String to convert. - * @return the converted string. + * @param src the String to convert + * @return the converted String */ @SuppressWarnings("PMD.AvoidReassigningLoopVariables") public static String toHtmlEntities(String src) { diff --git a/lib/src/main/java/rife/render/ShortenUrl.java b/lib/src/main/java/rife/render/ShortenUrl.java index 4a2c3f8..8340cc8 100644 --- a/lib/src/main/java/rife/render/ShortenUrl.java +++ b/lib/src/main/java/rife/render/ShortenUrl.java @@ -19,7 +19,6 @@ package rife.render; import rife.template.Template; import rife.template.ValueRenderer; -import rife.tools.StringUtils; /** *

Shortens a template value using is.gid. The value must a valid http or https URL.

@@ -34,7 +33,7 @@ import rife.tools.StringUtils; * * * @author Erik C. Thauvin -= * @since 1.0 + * @since 1.0 */ public class ShortenUrl implements ValueRenderer { /** diff --git a/lib/src/test/java/rife/render/TestEncode.java b/lib/src/test/java/rife/render/TestEncode.java index f6fea39..f6f4144 100644 --- a/lib/src/test/java/rife/render/TestEncode.java +++ b/lib/src/test/java/rife/render/TestEncode.java @@ -100,16 +100,4 @@ class TestEncode { t.setAttribute(TestCase.FOO, "a test &"); assertThat(t.getContent()).isEqualTo("\n a test &\n"); } - - @Test - void testShortenUrl() { - var t = TemplateFactory.HTML.get("shortenUrl"); - var url = "https://example.com/"; - var shortUrl = "https://is.gd/AG3Hwv"; - t.setValue(TestCase.FOO, url); - assertThat(t.getContent()).isEqualTo(String.format("%s", shortUrl, url)); - t.setValue(TestCase.FOO, TestCase.FOO); - assertThat(t.getContent()).isEqualTo("foo"); - - } } \ No newline at end of file diff --git a/lib/src/test/java/rife/render/TestFormat.java b/lib/src/test/java/rife/render/TestFormat.java new file mode 100644 index 0000000..7153c68 --- /dev/null +++ b/lib/src/test/java/rife/render/TestFormat.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package rife.render; + +import org.junit.jupiter.api.Test; +import rife.template.TemplateFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class TestFormat { + @Test + void testFormatCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "4342 2565 6244 0179"); + assertThat(t.getContent()).as("US VISA").isEqualTo("0179"); + t.setAttribute(TestCase.FOO, "5130-3899-9169-8324"); + assertThat(t.getContent()).as("FR MASTERCARD").isEqualTo("8324"); + t.setAttribute(TestCase.FOO, "374380141731053"); + assertThat(t.getContent()).as("UK AMEX").isEqualTo("1053"); + t.setAttribute(TestCase.FOO, "000000000000001"); + assertThat(t.getContent()).isEmpty(); + } + + @Test + void testShortenUrl() { + var t = TemplateFactory.HTML.get("shortenUrl"); + var url = "https://example.com/"; + var shortUrl = "https://is.gd/AG3Hwv"; + t.setValue(TestCase.FOO, url); + assertThat(t.getContent()).isEqualTo(String.format("%s", shortUrl, url)); + t.setValue(TestCase.FOO, TestCase.FOO); + assertThat(t.getContent()).isEqualTo("foo"); + + } +} diff --git a/lib/src/test/resources/templates/formatCreditCard.txt b/lib/src/test/resources/templates/formatCreditCard.txt new file mode 100644 index 0000000..973da5e --- /dev/null +++ b/lib/src/test/resources/templates/formatCreditCard.txt @@ -0,0 +1 @@ +{{v render:rife.render.FormatCreditCard:foo/}} \ No newline at end of file