Masks characters of a template value.
- * - *Usage:
- * - *- * <!--v render:rife.render.Mask:valueId/--> - * {{v render:rife.render.Mask:valueId/}} - *- * - * @author Erik C. Thauvin - * @see rife.render.Mask - * @since 1.0 - */ -public class Mask implements ValueRenderer { - /** - * {@inheritDoc} - */ - @Override - public String render(Template template, String valueId, String differentiator) { - var mask = "*"; - var unmasked = 0; - var fromStart = false; - var defaultValue = template.getDefaultValue(valueId); - if (defaultValue != null) { - var properties = new Properties(); - try { - properties.load(new StringReader(defaultValue)); - mask = properties.getProperty("mask", mask); - unmasked = Integer.parseInt(properties.getProperty("unmasked", "0")); - fromStart = "true".equalsIgnoreCase(properties.getProperty("fromStart", "false")); - } catch (IOException | NumberFormatException ignore) { - // do nothing - } - } - return RenderUtils.mask(template.getValueOrAttribute(differentiator), mask, unmasked, fromStart); - } -} diff --git a/lib/src/test/java/rife/render/TestRenderUtils.java b/lib/src/test/java/rife/render/TestRenderUtils.java deleted file mode 100644 index 1ef7762..0000000 --- a/lib/src/test/java/rife/render/TestRenderUtils.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 static org.assertj.core.api.Assertions.assertThat; - -class TestRenderUtils { - static final String SAMPLE_GERMAN = "Möchten Sie ein paar Äpfel?"; - - @Test - void testAbbreviate() { - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 9, "")).as("max=9") - .isEqualTo("This is a"); - - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 0, "")).as("max=0").isEmpty(); - - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, -1, "")).as("max=-1") - .isEqualTo(TestCase.SAMPLE_TEXT); - } - - @Test - void testCapitalize() { - assertThat(RenderUtils.capitalize("a")).isEqualTo("A"); - assertThat(RenderUtils.capitalize("")).as("empty").isEqualTo(""); - } - - @Test - void testHtmlEntities() { - assertThat(RenderUtils.htmlEntities(SAMPLE_GERMAN)) - .isEqualTo("Möchten Sie ein paar Äpfel?"); - } - - @Test - void testMask() { - var foo = "4342256562440179"; - - assertThat(RenderUtils.mask(foo, "?", 4, false)).as("mask=?") - .isEqualTo("????????????0179"); - - assertThat(RenderUtils.mask(foo, "-", 22, true)).as("unmasked=22") - .isEqualTo("----------------"); - - assertThat(RenderUtils.mask(foo, "•", -1, false)).as("mask=•") - .isEqualTo("••••••••••••••••"); - } - - @Test - void testNormalize() { - assertThat(RenderUtils.normalize(SAMPLE_GERMAN)).isEqualTo("mochten-sie-ein-paar-apfel"); - } - - @Test - void testRot13() { - var encoded = "Zöpugra Fvr rva cnne Äcsry?"; - assertThat(RenderUtils.rot13(SAMPLE_GERMAN)).as("encode").isEqualTo(encoded); - assertThat(RenderUtils.rot13(encoded)).as("decode").isEqualTo(SAMPLE_GERMAN); - } - - @Test - void testSwapCase() { - assertThat(RenderUtils.swapCase(SAMPLE_GERMAN)).isEqualTo("mÖCHTEN sIE EIN PAAR äPFEL?"); - } - - @Test - void testUcapitalize() { - assertThat(RenderUtils.uncapitalize("A")).isEqualTo("a"); - assertThat(RenderUtils.uncapitalize("")).as("empty").isEqualTo(""); - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index fc4b198..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = "rife2-template-renderers" -include("lib") diff --git a/src/bld/java/rife/render/TemplateRenderersBuild.java b/src/bld/java/rife/render/TemplateRenderersBuild.java new file mode 100644 index 0000000..4f4fc5e --- /dev/null +++ b/src/bld/java/rife/render/TemplateRenderersBuild.java @@ -0,0 +1,125 @@ +/* + * Copyright 2023-2024 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.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.extension.JacocoReportOperation; +import rife.bld.extension.PmdOperation; +import rife.bld.extension.TestsBadgeOperation; +import rife.bld.publish.PublishDeveloper; +import rife.bld.publish.PublishInfo; +import rife.bld.publish.PublishLicense; +import rife.bld.publish.PublishScm; + +import java.util.List; + +import static rife.bld.dependencies.Repository.*; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; +import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING; + +public class TemplateRenderersBuild extends Project { + private final TestsBadgeOperation testsBadgeOperation = new TestsBadgeOperation(); + + public TemplateRenderersBuild() { + pkg = "rife.render"; + name = "rife2-template-renderers"; + version = version(1, 2, 1, "SNAPSHOT"); + + javaRelease = 17; + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); + + scope(compile) + .include(dependency("com.uwyn.rife2", "rife2", version(1, 9, 1))); + scope(test) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 0))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 0))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); + + javadocOperation().javadocOptions() + .docTitle("RIFE2 Template Renderers") + .author() + .docLint(NO_MISSING) + .link("https://rife2.github.io/rife2/"); + + publishOperation() + .repository(version.isSnapshot() ? SONATYPE_SNAPSHOTS + .withCredentials(property("sonatypeUser"), property("sonatypePassword")) + : SONATYPE_RELEASES + .withCredentials(property("sonatypeUser"), property("sonatypePassword"))) + .repository(version.isSnapshot() ? RIFE2_SNAPSHOTS + .withCredentials(property("rife2Username"), property("rife2Password")) + : RIFE2_RELEASES + .withCredentials(property("rife2Username"), property("rife2Password"))) + .info(new PublishInfo() + .groupId("com.uwyn.rife2") + .artifactId("rife2-renderers") + .name("RIFE2 Template Renderers") + .description("Template Renderers for the RIFE2 web framework") + .url("https://github.com/rife2/rife2-template-renderers") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/")) + .developer(new PublishDeveloper() + .id("gbevin") + .name("Geert Bevin") + .email("gbevin@uwyn.com") + .url("https://github.com/gbevin")) + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt")) + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/rife2-template-renderers.git") + .developerConnection("scm:git:git@github.com:rife2/rife2-template-renderers.git") + .url("https://github.com/rife2/rife2-template-renderers")) + .signKey(property("signKey")) + .signPassphrase(property("signPassphrase"))); + } + + public static void main(String[] args) { + new TemplateRenderersBuild().start(args); + } + + @BuildCommand(summary = "Generates JaCoCo Reports") + public void jacoco() throws Exception { + new JacocoReportOperation() + .fromProject(this) + .execute(); + } + + @BuildCommand(summary = "Runs PMD analysis") + public void pmd() throws Exception { + new PmdOperation() + .fromProject(this) + .failOnViolation(true) + .ruleSets("config/pmd.xml") + .execute(); + } + + public void test() throws Exception { + testsBadgeOperation.executeOnce(() -> testsBadgeOperation + .url(property("testsBadgeUrl")) + .apiKey(property("testsBadgeApiKey")) + .fromProject(this)); + } +} diff --git a/lib/src/main/java/rife/render/Abbreviate.java b/src/main/java/rife/render/Abbreviate.java similarity index 53% rename from lib/src/main/java/rife/render/Abbreviate.java rename to src/main/java/rife/render/Abbreviate.java index cfe679a..dfacacb 100644 --- a/lib/src/main/java/rife/render/Abbreviate.java +++ b/src/main/java/rife/render/Abbreviate.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -20,12 +20,8 @@ package rife.render; import rife.template.Template; import rife.template.ValueRenderer; -import java.io.IOException; -import java.io.StringReader; -import java.util.Properties; - /** - *
Abbreviate a template value with ellipses.
+ *Abbreviates a template value with ellipses.
* *Usage:
* @@ -40,7 +36,18 @@ import java.util.Properties; */ public class Abbreviate implements ValueRenderer { /** - * {@inheritDoc} + *Returns the template value abbreviated with ellipses.
+ * + *Two parameters can be specified:
+ *mark
: the string that will be used to abbreviate the value. Default is ...
max
: the maximum number of characters to render. Default is -1
(no abbreviation).Capitalizes a template value.
@@ -36,10 +37,15 @@ import rife.template.ValueRenderer; */ public class Capitalize implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value by capitalizing it. + * + * @param template the template containing the value to be rendered + * @param valueId the identifier of the value to render + * @param differentiator a string used to differentiate the rendering + * @return the capitalized and encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.capitalize(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(StringUtils.capitalize(template.getValueOrAttribute(differentiator))); } } diff --git a/src/main/java/rife/render/CapitalizeWords.java b/src/main/java/rife/render/CapitalizeWords.java new file mode 100644 index 0000000..e95e6f8 --- /dev/null +++ b/src/main/java/rife/render/CapitalizeWords.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023-2024 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; + +/** + *Capitalizes words of a template value.
+ * + *Usage:
+ * + *+ * <!--v render:rife.render.CapitalizeWords:valueId/--> + * {{v render:rife.render.CapitalizeWords:valueId/}} + *+ * + * @author Erik C. Thauvin + * @see rife.render.CapitalizeWords + * @since 1.2 + */ +public class CapitalizeWords implements ValueRenderer { + /** + * Returns the template value by capitalizing it. + * + * @param template the template containing the value to be rendered + * @param valueId the identifier of the value to render + * @param differentiator a string used to differentiate the rendering + * @return the capitalized and encoded value + */ + @Override + public String render(Template template, String valueId, String differentiator) { + return template.getEncoder().encode(RenderUtils.capitalizeWords(template.getValueOrAttribute(differentiator))); + } +} diff --git a/lib/src/main/java/rife/render/DateIso.java b/src/main/java/rife/render/DateIso.java similarity index 62% rename from lib/src/main/java/rife/render/DateIso.java rename to src/main/java/rife/render/DateIso.java index b261268..0da04e6 100644 --- a/lib/src/main/java/rife/render/DateIso.java +++ b/src/main/java/rife/render/DateIso.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -23,7 +23,7 @@ import rife.template.ValueRenderer; import java.time.ZonedDateTime; /** - *
Return the current date in ISO 8601 format.
+ *Renders the current date in ISO 8601 format.
* *Usage:
* @@ -38,10 +38,15 @@ import java.time.ZonedDateTime; */ public class DateIso implements ValueRenderer { /** - * {@inheritDoc} + * Returns the current date in ISO 8601 format, encoded according to the template's encoding rules. + * + * @param template the template that is currently being rendered + * @param valueId the value id that triggers the rendering of this value renderer + * @param differentiator a differentiator that may be used to differentiate the rendering of this value renderer + * @return the current date in ISO 8601 format, encoded according to the template's encoding rules */ @Override public String render(Template template, String valueId, String differentiator) { - return ZonedDateTime.now().format(RenderUtils.ISO_8601_DATE_FORMATTER); + return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.ISO_8601_DATE_FORMATTER)); } } diff --git a/lib/src/main/java/rife/render/DateTimeIso.java b/src/main/java/rife/render/DateTimeIso.java similarity index 54% rename from lib/src/main/java/rife/render/DateTimeIso.java rename to src/main/java/rife/render/DateTimeIso.java index 683abb7..e8513bb 100644 --- a/lib/src/main/java/rife/render/DateTimeIso.java +++ b/src/main/java/rife/render/DateTimeIso.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -20,14 +20,11 @@ package rife.render; import rife.template.Template; import rife.template.ValueRenderer; -import java.io.IOException; -import java.io.StringReader; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Properties; /** - *Return the current date and time in ISO 8601 format.
+ *Renders the current date and time in ISO 8601 format.
* *Usage:
* @@ -42,25 +39,28 @@ import java.util.Properties; */ public class DateTimeIso implements ValueRenderer { /** - * {@inheritDoc} + * Renders the current date and time in ISO 8601 format. + * + *Additionally, it allows specifying a time zone through the template's default value properties with the key + * {@code tz}. If no time zone is specified, the system default time zone is used.
+ * + * @param template the template that is currently being rendered + * @param valueId the id of the value to be rendered + * @param differentiator a differentiator that may be used to differentiate the rendering of this value renderer + * @return the current date and time in ISO 8601 format */ @Override public String render(Template template, String valueId, String differentiator) { var defaultValue = template.getDefaultValue(valueId); if (defaultValue != null) { - var properties = new Properties(); - try { - var tz = "tz"; - properties.load(new StringReader(defaultValue)); - if (properties.containsKey(tz)) { - return ZonedDateTime.now().format( - RenderUtils.ISO_8601_FORMATTER.withZone(ZoneId.of(properties.getProperty(tz)))); - } - } catch (IOException ignore) { - // do nothing + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + var tz = "tz"; + if (properties.containsKey(tz)) { + return ZonedDateTime.now().format( + RenderUtils.ISO_8601_FORMATTER.withZone(ZoneId.of(properties.getProperty(tz)))); } } - return ZonedDateTime.now().format(RenderUtils.ISO_8601_FORMATTER); + return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.ISO_8601_FORMATTER)); } } diff --git a/lib/src/main/java/rife/render/DateTimeRfc2822.java b/src/main/java/rife/render/DateTimeRfc2822.java similarity index 71% rename from lib/src/main/java/rife/render/DateTimeRfc2822.java rename to src/main/java/rife/render/DateTimeRfc2822.java index 5058b70..2bdbd54 100644 --- a/lib/src/main/java/rife/render/DateTimeRfc2822.java +++ b/src/main/java/rife/render/DateTimeRfc2822.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -23,7 +23,7 @@ import rife.template.ValueRenderer; import java.time.ZonedDateTime; /** - *Return the current date and time in RFC 2822 format.
+ *Renders the current date and time in RFC 2822 format.
* *Usage:
* @@ -38,10 +38,15 @@ import java.time.ZonedDateTime; */ public class DateTimeRfc2822 implements ValueRenderer { /** - * {@inheritDoc} + * Returns the current date and time in RFC 2822 format. + * + * @param template the template instance + * @param valueId the value id + * @param differentiator the differentiator + * @return the current date and time in RFC 2822 format */ @Override public String render(Template template, String valueId, String differentiator) { - return ZonedDateTime.now().format(RenderUtils.RFC_2822_FORMATTER); + return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.RFC_2822_FORMATTER)); } } diff --git a/lib/src/main/java/rife/render/EncodeBase64.java b/src/main/java/rife/render/EncodeBase64.java similarity index 69% rename from lib/src/main/java/rife/render/EncodeBase64.java rename to src/main/java/rife/render/EncodeBase64.java index c5bd854..e00b24c 100644 --- a/lib/src/main/java/rife/render/EncodeBase64.java +++ b/src/main/java/rife/render/EncodeBase64.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -40,11 +40,18 @@ import java.nio.charset.StandardCharsets; */ public class EncodeBase64 implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to Base64. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the Base64-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return StringUtils.encodeBase64(template.getValueOrAttribute(differentiator) - .getBytes(StandardCharsets.UTF_8)); + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + return RenderUtils.encode( + StringUtils.encodeBase64(template.getValueOrAttribute(differentiator).getBytes(StandardCharsets.UTF_8)), + properties); } } diff --git a/lib/src/main/java/rife/render/EncodeHtml.java b/src/main/java/rife/render/EncodeHtml.java similarity index 79% rename from lib/src/main/java/rife/render/EncodeHtml.java rename to src/main/java/rife/render/EncodeHtml.java index 858b6dd..b67959d 100644 --- a/lib/src/main/java/rife/render/EncodeHtml.java +++ b/src/main/java/rife/render/EncodeHtml.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,7 +38,12 @@ import rife.tools.StringUtils; */ public class EncodeHtml implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to HTML. + * + * @param template the template containing the value to be rendered + * @param valueId the identifier of the value to render + * @param differentiator a string used to differentiate the rendering + * @return the HTML-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { diff --git a/lib/src/main/java/rife/render/EncodeHtmlEntities.java b/src/main/java/rife/render/EncodeHtmlEntities.java similarity index 82% rename from lib/src/main/java/rife/render/EncodeHtmlEntities.java rename to src/main/java/rife/render/EncodeHtmlEntities.java index 4f84839..cee3e8b 100644 --- a/lib/src/main/java/rife/render/EncodeHtmlEntities.java +++ b/src/main/java/rife/render/EncodeHtmlEntities.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -36,7 +36,12 @@ import rife.template.ValueRenderer; */ public class EncodeHtmlEntities implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to HTML decimal entities. + * + * @param template the template instance + * @param valueId the value id + * @param differentiator the differentiator + * @return the encoded value */ @Override public String render(Template template, String valueId, String differentiator) { diff --git a/lib/src/main/java/rife/render/EncodeJs.java b/src/main/java/rife/render/EncodeJs.java similarity index 70% rename from lib/src/main/java/rife/render/EncodeJs.java rename to src/main/java/rife/render/EncodeJs.java index caafb18..27de902 100644 --- a/lib/src/main/java/rife/render/EncodeJs.java +++ b/src/main/java/rife/render/EncodeJs.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,10 +38,16 @@ import rife.tools.StringUtils; */ public class EncodeJs implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to JavaScript/ECMAScript. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the JavaScript/ECMAScript-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.encodeJs(template.getValueOrAttribute(differentiator)); + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + return RenderUtils.encode(RenderUtils.encodeJs(template.getValueOrAttribute(differentiator)), properties); } } diff --git a/lib/src/main/java/rife/render/EncodeJson.java b/src/main/java/rife/render/EncodeJson.java similarity index 71% rename from lib/src/main/java/rife/render/EncodeJson.java rename to src/main/java/rife/render/EncodeJson.java index 60916f6..8e17b29 100644 --- a/lib/src/main/java/rife/render/EncodeJson.java +++ b/src/main/java/rife/render/EncodeJson.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,10 +38,16 @@ import rife.tools.StringUtils; */ public class EncodeJson implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to JSON. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the JSON-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return StringUtils.encodeJson(template.getValueOrAttribute(differentiator)); + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + return RenderUtils.encode(StringUtils.encodeJson(template.getValueOrAttribute(differentiator)), properties); } } diff --git a/lib/src/main/java/rife/render/EncodeUnicode.java b/src/main/java/rife/render/EncodeUnicode.java similarity index 70% rename from lib/src/main/java/rife/render/EncodeUnicode.java rename to src/main/java/rife/render/EncodeUnicode.java index 1d67bf1..afd2a48 100644 --- a/lib/src/main/java/rife/render/EncodeUnicode.java +++ b/src/main/java/rife/render/EncodeUnicode.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,10 +38,16 @@ import rife.tools.StringUtils; */ public class EncodeUnicode implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to Unicode escape codes. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the Unicode escape codes-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return StringUtils.encodeUnicode(template.getValueOrAttribute(differentiator)); + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + return RenderUtils.encode(StringUtils.encodeUnicode(template.getValueOrAttribute(differentiator)), properties); } } diff --git a/lib/src/main/java/rife/render/EncodeUrl.java b/src/main/java/rife/render/EncodeUrl.java similarity index 71% rename from lib/src/main/java/rife/render/EncodeUrl.java rename to src/main/java/rife/render/EncodeUrl.java index f6201d5..b977353 100644 --- a/lib/src/main/java/rife/render/EncodeUrl.java +++ b/src/main/java/rife/render/EncodeUrl.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,10 +38,16 @@ import rife.tools.StringUtils; */ public class EncodeUrl implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to URL. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the URL-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { - return StringUtils.encodeUrl(template.getValueOrAttribute(differentiator)); + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + return RenderUtils.encode(StringUtils.encodeUrl(template.getValueOrAttribute(differentiator)), properties); } } diff --git a/lib/src/main/java/rife/render/EncodeXml.java b/src/main/java/rife/render/EncodeXml.java similarity index 81% rename from lib/src/main/java/rife/render/EncodeXml.java rename to src/main/java/rife/render/EncodeXml.java index 743e084..00b0faa 100644 --- a/lib/src/main/java/rife/render/EncodeXml.java +++ b/src/main/java/rife/render/EncodeXml.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,7 +38,12 @@ import rife.tools.StringUtils; */ public class EncodeXml implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded to XML. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the XML-encoded value */ @Override public String render(Template template, String valueId, String differentiator) { diff --git a/lib/src/main/java/rife/render/FormatCreditCard.java b/src/main/java/rife/render/FormatCreditCard.java similarity index 75% rename from lib/src/main/java/rife/render/FormatCreditCard.java rename to src/main/java/rife/render/FormatCreditCard.java index 840d66e..fec124d 100644 --- a/lib/src/main/java/rife/render/FormatCreditCard.java +++ b/src/main/java/rife/render/FormatCreditCard.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -36,10 +36,15 @@ import rife.template.ValueRenderer; */ public class FormatCreditCard implements ValueRenderer { /** - * {@inheritDoc} + * Returns the last 4 digits of the template credit number value. + * + * @param template the {@link Template} + * @param valueId the value id + * @param differentiator the differentiator + * @return the formatted value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.formatCreditCard(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(RenderUtils.formatCreditCard(template.getValueOrAttribute(differentiator))); } } diff --git a/lib/src/main/java/rife/render/Lowercase.java b/src/main/java/rife/render/Lowercase.java similarity index 77% rename from lib/src/main/java/rife/render/Lowercase.java rename to src/main/java/rife/render/Lowercase.java index d966142..6f9f2ec 100644 --- a/lib/src/main/java/rife/render/Lowercase.java +++ b/src/main/java/rife/render/Lowercase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -37,7 +37,12 @@ import rife.tools.Localization; */ public class Lowercase implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value converted to lowercase. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the lowercase value */ @Override public String render(Template template, String valueId, String differentiator) { @@ -45,6 +50,6 @@ public class Lowercase implements ValueRenderer { if (value == null || value.isBlank()) { return value; } - return value.toLowerCase(Localization.getLocale()); + return template.getEncoder().encode(value.toLowerCase(Localization.getLocale())); } } diff --git a/src/main/java/rife/render/Mask.java b/src/main/java/rife/render/Mask.java new file mode 100644 index 0000000..fe0a709 --- /dev/null +++ b/src/main/java/rife/render/Mask.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023-2024 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; + +/** + *Masks characters of a template value.
+ * + *Usage:
+ * + *+ * <!--v render:rife.render.Mask:valueId/--> + * {{v render:rife.render.Mask:valueId/}} + *+ * + * @author Erik C. Thauvin + * @see rife.render.Mask + * @since 1.0 + */ +public class Mask implements ValueRenderer { + + /** + *
Renders a template value with characters of the value masked using the specified mask.
+ * + *The mask is specified as a template default value with the following syntax:
+ * + *+ * mask=<mask>[,unmasked=<unmasked>][,fromStart=<fromStart>] + *+ * + *
Where:
+ * + **
0
false
Generates an SVG QR Code for a template value using goQR.me.
* @@ -40,21 +36,17 @@ import java.util.Properties; */ public class QrCode implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value encoded as an SVG QR Code. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the SVG QR Code */ @Override public String render(Template template, String valueId, String differentiator) { - var size = "150x150"; - var defaultValue = template.getDefaultValue(valueId); - if (defaultValue != null) { - var properties = new Properties(); - try { - properties.load(new StringReader(defaultValue)); - size = properties.getProperty("size", size); - } catch (IOException ignore) { - // do nothing - } - } + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + var size = properties.getProperty("size", "150x150"); return RenderUtils.qrCode(template.getValueOrAttribute(differentiator), size); } } diff --git a/lib/src/main/java/rife/render/RenderUtils.java b/src/main/java/rife/render/RenderUtils.java similarity index 61% rename from lib/src/main/java/rife/render/RenderUtils.java rename to src/main/java/rife/render/RenderUtils.java index f856408..c05f5b9 100644 --- a/lib/src/main/java/rife/render/RenderUtils.java +++ b/src/main/java/rife/render/RenderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -21,16 +21,19 @@ import rife.tools.Localization; import rife.tools.StringUtils; import java.io.IOException; +import java.io.StringReader; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.text.Normalizer; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoField; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Collection of utility-type methods commonly used by the renderers. @@ -39,6 +42,10 @@ import java.util.concurrent.TimeUnit; * @since 1.0 */ public final class RenderUtils { + /** + * The encoding property. + */ + public static final String ENCODING_PROPERTY = "encoding"; /** * ISO 8601 date formatter. * @@ -76,18 +83,19 @@ public final class RenderUtils { DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss zzz").withLocale(Localization.getLocale()); private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"; + private static final Logger LOGGER = Logger.getLogger(RenderUtils.class.getName()); private RenderUtils() { // no-op } /** - * Abbreviates a String to the given length using a replacement marker. + * Abbreviates a {@code String} to the given length using a replacement marker. * - * @param src the source String - * @param max the maximum length of the resulting String - * @param marker the String used as a replacement marker - * @return the abbreviated String + * @param src the source {@code String} + * @param max the maximum length of the resulting {@code String} + * @param marker the {@code String} used as a replacement marker + * @return the abbreviated {@code String} */ public static String abbreviate(String src, int max, String marker) { if (src == null || src.isBlank() || marker == null) { @@ -106,34 +114,102 @@ public final class RenderUtils { /** * Returns the Swatch Internet (.beat) Time for the give date-time. * - * @param zonedDateTime the date and time. + * @param zonedDateTime the date and time * @return the .beat time. (eg.: {@code @248}) */ public static String beatTime(ZonedDateTime zonedDateTime) { var zdt = zonedDateTime.withZoneSameInstant(ZoneId.of("UTC+01:00")); - var beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60) + - (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); + var beats = (int) ((zdt.getSecond() + (zdt.getMinute() * 60) + + (zdt.getHour() * 3600)) / 86.4); return String.format("@%03d", beats); } /** - * Capitalizes a String. + * Returns a {@code String} with the first letter of each word capitalized. * - * @param src the source String - * @return the capitalized String + * @param src the source {@code String} + * @return the capitalized {@code String} */ - public static String capitalize(String src) { + public static String capitalizeWords(String src) { if (src == null || src.isBlank()) { return src; } - return src.substring(0, 1).toUpperCase(Localization.getLocale()) + src.substring(1); + + var result = new StringBuilder(); + var capitalizeNext = true; + + for (var i = 0; i < src.length(); i++) { + var c = src.charAt(i); + if (Character.isWhitespace(c)) { + capitalizeNext = true; + result.append(c); + } else { + if (capitalizeNext) { + result.append(Character.toUpperCase(c)); + } else { + result.append(Character.toLowerCase(c)); + } + capitalizeNext = false; + } + } + + return result.toString(); } /** - * Encodes a String to JavaScript/ECMAScript. + *Encodes the source {@code String} to the specified encoding.
* - * @param src the source String - * @return the encoded String + *The supported encodings are:
+ * + *Returns the last 4 digits a credit card number.
+ * + *Shortens a URL using is.gid.
* - *The URL String must be a valid http or https URL.
+ *The URL {@code String} must be a valid http or https URL.
* *Based on isgd-shorten
* @@ -409,8 +503,8 @@ public final class RenderUtils { /** * Swaps the case of a String. * - * @param src the String to swap the case of - * @return the modified String or null + * @param src the {@code String} to swap the case of + * @return the modified {@code String} or null */ @SuppressWarnings("PMD.AvoidReassigningLoopVariables") public static String swapCase(String src) { @@ -438,19 +532,6 @@ public final class RenderUtils { return new String(buff, 0, offset); } - /** - * Uncapitalizes a String. - * - * @param src the source String - * @return the uncapitalized String - */ - public static String uncapitalize(String src) { - if (src == null || src.isBlank()) { - return src; - } - return src.substring(0, 1).toLowerCase(Localization.getLocale()) + src.substring(1); - } - /** *Returns the formatted server uptime.
* @@ -473,7 +554,7 @@ public final class RenderUtils { * * @param uptime the uptime in milliseconds * @param properties the format properties - * @return the formatted uptime. + * @return the formatted uptime */ @SuppressWarnings("UnnecessaryUnicodeEscape") public static String uptime(long uptime, Properties properties) { @@ -520,4 +601,43 @@ public final class RenderUtils { return sb.toString(); } + + /** + * Validates a credit card number using the Luhn algorithm. + * + * @param cc the credit card number + * @return {@code true} if the credit card number is valid + */ + public static boolean validateCreditCard(String cc) { + try { + var len = cc.length(); + if (len >= 8 && len <= 19) { + // Luhn algorithm + var sum = 0; + boolean second = false; + int digit; + char c; + for (int i = len - 1; i >= 0; i--) { + c = cc.charAt(i); + if (c >= '0' && c <= '9') { + digit = cc.charAt(i) - '0'; + if (second) { + digit = digit * 2; + } + sum += digit / 10; + sum += digit % 10; + + second = !second; + } + } + if (sum % 10 == 0) { + return true; + } + } + } catch (NumberFormatException ignored) { + // do nothing + } + return false; + } + } \ No newline at end of file diff --git a/lib/src/main/java/rife/render/Rot13.java b/src/main/java/rife/render/Rot13.java similarity index 74% rename from lib/src/main/java/rife/render/Rot13.java rename to src/main/java/rife/render/Rot13.java index 4bca6db..75e3dd7 100644 --- a/lib/src/main/java/rife/render/Rot13.java +++ b/src/main/java/rife/render/Rot13.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -36,10 +36,15 @@ import rife.template.ValueRenderer; */ public class Rot13 implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value translated to/from ROT13. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the ROT13 value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.rot13(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(RenderUtils.rot13(template.getValueOrAttribute(differentiator))); } } diff --git a/lib/src/main/java/rife/render/ShortenUrl.java b/src/main/java/rife/render/ShortenUrl.java similarity index 74% rename from lib/src/main/java/rife/render/ShortenUrl.java rename to src/main/java/rife/render/ShortenUrl.java index b52c686..204d7c6 100644 --- a/lib/src/main/java/rife/render/ShortenUrl.java +++ b/src/main/java/rife/render/ShortenUrl.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -38,10 +38,15 @@ import rife.template.ValueRenderer; */ public class ShortenUrl implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value shortened using is.gid. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the template shortened value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.shortenUrl(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(RenderUtils.shortenUrl(template.getValueOrAttribute(differentiator))); } } diff --git a/lib/src/main/java/rife/render/SwapCase.java b/src/main/java/rife/render/SwapCase.java similarity index 71% rename from lib/src/main/java/rife/render/SwapCase.java rename to src/main/java/rife/render/SwapCase.java index 270f1f6..1fd996a 100644 --- a/lib/src/main/java/rife/render/SwapCase.java +++ b/src/main/java/rife/render/SwapCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -21,7 +21,7 @@ import rife.template.Template; import rife.template.ValueRenderer; /** - *Swap case of a template value.
+ *Swaps case of a template value.
* *Usage:
* @@ -35,12 +35,16 @@ import rife.template.ValueRenderer; * @since 1.0 */ public class SwapCase implements ValueRenderer { - /** - * {@inheritDoc} + * Returns the template value with swapped case. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the swapped case value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.swapCase(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(RenderUtils.swapCase(template.getValueOrAttribute(differentiator))); } } diff --git a/lib/src/main/java/rife/render/TimeIso.java b/src/main/java/rife/render/TimeIso.java similarity index 67% rename from lib/src/main/java/rife/render/TimeIso.java rename to src/main/java/rife/render/TimeIso.java index e1f0023..01bf5b2 100644 --- a/lib/src/main/java/rife/render/TimeIso.java +++ b/src/main/java/rife/render/TimeIso.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -23,7 +23,7 @@ import rife.template.ValueRenderer; import java.time.ZonedDateTime; /** - *Return the current time in ISO 8601 format.
+ *Renders the current time in ISO 8601 format.
* *Usage:
* @@ -38,10 +38,15 @@ import java.time.ZonedDateTime; */ public class TimeIso implements ValueRenderer { /** - * {@inheritDoc} + * Returns the current time in ISO 8601 format. + * + * @param template the template that is currently being rendered + * @param valueId the id of the value to be rendered + * @param differentiator a differentiator that may be used to differentiate the rendering of this value renderer + * @return the current time in ISO 8601 format */ @Override public String render(Template template, String valueId, String differentiator) { - return ZonedDateTime.now().format(RenderUtils.ISO_8601_TIME_FORMATTER); + return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.ISO_8601_TIME_FORMATTER)); } } diff --git a/lib/src/main/java/rife/render/Trim.java b/src/main/java/rife/render/Trim.java similarity index 74% rename from lib/src/main/java/rife/render/Trim.java rename to src/main/java/rife/render/Trim.java index 53a66e1..a0951b4 100644 --- a/lib/src/main/java/rife/render/Trim.java +++ b/src/main/java/rife/render/Trim.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -36,7 +36,12 @@ import rife.template.ValueRenderer; */ public class Trim implements ValueRenderer { /** - * {@inheritDoc} + * Renders the template value by removing leading and trailing whitespace. + * + * @param template the template instance + * @param valueId the id of the value to render + * @param differentiator an optional differentiator to use for cache invalidation + * @return the trimmed value, or the original value if it is {@code null} or empty */ @Override public String render(Template template, String valueId, String differentiator) { @@ -44,6 +49,6 @@ public class Trim implements ValueRenderer { if (value == null || value.isEmpty()) { return value; } - return value.trim(); + return template.getEncoder().encode(value.trim()); } } diff --git a/lib/src/main/java/rife/render/Uncapitalize.java b/src/main/java/rife/render/Uncapitalize.java similarity index 72% rename from lib/src/main/java/rife/render/Uncapitalize.java rename to src/main/java/rife/render/Uncapitalize.java index 1e670a7..20b0aad 100644 --- a/lib/src/main/java/rife/render/Uncapitalize.java +++ b/src/main/java/rife/render/Uncapitalize.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -19,6 +19,7 @@ package rife.render; import rife.template.Template; import rife.template.ValueRenderer; +import rife.tools.StringUtils; /** *Un-capitalizes a template value.
@@ -36,10 +37,15 @@ import rife.template.ValueRenderer; */ public class Uncapitalize implements ValueRenderer { /** - * {@inheritDoc} + * Returns the un-capitalized template value. + * + * @param template the template to render + * @param valueId the id of the value to render + * @param differentiator the differentiator to use for the value lookup + * @return the un-capitalized value */ @Override public String render(Template template, String valueId, String differentiator) { - return RenderUtils.uncapitalize(template.getValueOrAttribute(differentiator)); + return template.getEncoder().encode(StringUtils.uncapitalize(template.getValueOrAttribute(differentiator))); } } diff --git a/lib/src/main/java/rife/render/Uppercase.java b/src/main/java/rife/render/Uppercase.java similarity index 74% rename from lib/src/main/java/rife/render/Uppercase.java rename to src/main/java/rife/render/Uppercase.java index bb45bb7..92e57a5 100644 --- a/lib/src/main/java/rife/render/Uppercase.java +++ b/src/main/java/rife/render/Uppercase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -22,7 +22,7 @@ import rife.template.ValueRenderer; import rife.tools.Localization; /** - *Convert a template value to uppercase.
+ *Converts a template value to uppercase.
* *Usage:
* @@ -37,7 +37,12 @@ import rife.tools.Localization; */ public class Uppercase implements ValueRenderer { /** - * {@inheritDoc} + * Returns the template value converted to uppercase. + * + * @param template the template that contains the value + * @param valueId the id of the value + * @param differentiator the differentiator to use + * @return the uppercased value */ @Override public String render(Template template, String valueId, String differentiator) { @@ -45,6 +50,6 @@ public class Uppercase implements ValueRenderer { if (value == null || value.isBlank()) { return value; } - return value.toUpperCase(Localization.getLocale()); + return template.getEncoder().encode(value.toUpperCase(Localization.getLocale())); } } diff --git a/lib/src/main/java/rife/render/Uptime.java b/src/main/java/rife/render/Uptime.java similarity index 63% rename from lib/src/main/java/rife/render/Uptime.java rename to src/main/java/rife/render/Uptime.java index 06b1371..14bb2de 100644 --- a/lib/src/main/java/rife/render/Uptime.java +++ b/src/main/java/rife/render/Uptime.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -20,10 +20,7 @@ package rife.render; import rife.template.Template; import rife.template.ValueRenderer; -import java.io.IOException; -import java.io.StringReader; import java.lang.management.ManagementFactory; -import java.util.Properties; /** * Renders the server uptime. @@ -41,24 +38,23 @@ import java.util.Properties; */ public class Uptime implements ValueRenderer { /** - * {@inheritDoc} + * Renders the server uptime. + * + * @param template the template that is currently being rendered + * @param valueId the id of the value to render + * @param differentiator a differentiator that may be used to differentiate the rendering of this value renderer + * @return the server uptime */ @Override public String render(Template template, String valueId, String differentiator) { - var properties = new Properties(); - var defaultValue = template.getDefaultValue(valueId); - if (defaultValue != null) { - try { - properties.load(new StringReader(defaultValue)); - } catch (IOException ignore) { - // ignore - } + var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId)); + String uptime; + if (template.hasAttribute(Uptime.class.getName())) { + uptime = RenderUtils.uptime((long) template.getAttribute(Uptime.class.getName()), properties); + } else { + uptime = RenderUtils.uptime(ManagementFactory.getRuntimeMXBean().getUptime(), properties); } - if (template.hasAttribute(Uptime.class.getName())) { - return RenderUtils.uptime((long) template.getAttribute(Uptime.class.getName()), properties); - } else { - return RenderUtils.uptime(ManagementFactory.getRuntimeMXBean().getUptime(), properties); - } + return template.getEncoder().encode(uptime); } } diff --git a/lib/src/main/java/rife/render/Year.java b/src/main/java/rife/render/Year.java similarity index 71% rename from lib/src/main/java/rife/render/Year.java rename to src/main/java/rife/render/Year.java index d146738..cb8c8f5 100644 --- a/lib/src/main/java/rife/render/Year.java +++ b/src/main/java/rife/render/Year.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -37,12 +37,16 @@ import java.time.ZonedDateTime; * @since 1.0 */ public class Year implements ValueRenderer { - /** - * {@inheritDoc} + * Renders the current year. + * + * @param template the template that is currently being rendered + * @param valueId the id of the value to render + * @param differentiator a differentiator that may be used to differentiate the rendering of this value renderer + * @return the current year */ @Override public String render(Template template, String valueId, String differentiator) { - return ZonedDateTime.now().format(RenderUtils.ISO_8601_YEAR_FORMATTER); + return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.ISO_8601_YEAR_FORMATTER)); } } diff --git a/src/test/java/rife/render/DisableOnCiCondition.java b/src/test/java/rife/render/DisableOnCiCondition.java new file mode 100644 index 0000000..27081c2 --- /dev/null +++ b/src/test/java/rife/render/DisableOnCiCondition.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023-2024 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.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * Disables tests on CI condition. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class DisableOnCiCondition implements ExecutionCondition { + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + if (System.getenv("CI") != null) { + return ConditionEvaluationResult.disabled("Test disabled on CI"); + } else { + return ConditionEvaluationResult.enabled("Test enabled"); + } + } +} diff --git a/src/test/java/rife/render/DisabledOnCi.java b/src/test/java/rife/render/DisabledOnCi.java new file mode 100644 index 0000000..8c369d6 --- /dev/null +++ b/src/test/java/rife/render/DisabledOnCi.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023-2024 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.extension.ExtendWith; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Disables tests on CI annotation. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(DisableOnCiCondition.class) +public @interface DisabledOnCi { +} diff --git a/lib/src/test/java/rife/render/TestCase.java b/src/test/java/rife/render/TestCase.java similarity index 86% rename from lib/src/test/java/rife/render/TestCase.java rename to src/test/java/rife/render/TestCase.java index cebd066..2759ff6 100644 --- a/lib/src/test/java/rife/render/TestCase.java +++ b/src/test/java/rife/render/TestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -40,6 +40,9 @@ class TestCase { var bean = new ValueBean("this IS a TEST."); t.setBean(bean); assertThat(t.getContent()).isEqualTo(bean.getValue() + ": this is a test."); + bean = new ValueBean(""); + t.setBean(bean); + assertThat(t.getContent()).isEqualTo(bean.getValue() + ": "); } @Test @@ -54,6 +57,8 @@ class TestCase { var t = TemplateFactory.TXT.get("trim"); t.setAttribute(FOO, "\t" + SAMPLE_TEXT + " \n"); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT); + t.setAttribute(FOO, ""); + assertThat(t.getContent()).isEmpty(); } @Test @@ -68,5 +73,7 @@ class TestCase { var t = TemplateFactory.TXT.get("uppercase"); t.setAttribute("bar", SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT.toUpperCase(Localization.getLocale())); + t.setAttribute("bar", ""); + assertThat(t.getContent()).isEmpty(); } } \ No newline at end of file diff --git a/lib/src/test/java/rife/render/TestDateTime.java b/src/test/java/rife/render/TestDateTime.java similarity index 97% rename from lib/src/test/java/rife/render/TestDateTime.java rename to src/test/java/rife/render/TestDateTime.java index 55bb519..bfb8cc7 100644 --- a/lib/src/test/java/rife/render/TestDateTime.java +++ b/src/test/java/rife/render/TestDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. diff --git a/lib/src/test/java/rife/render/TestEncode.java b/src/test/java/rife/render/TestEncode.java similarity index 66% rename from lib/src/test/java/rife/render/TestEncode.java rename to src/test/java/rife/render/TestEncode.java index c5bc8bd..bc390d4 100644 --- a/lib/src/test/java/rife/render/TestEncode.java +++ b/src/test/java/rife/render/TestEncode.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2024 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. @@ -28,6 +28,10 @@ class TestEncode { var t = TemplateFactory.TXT.get("encodeBase64"); t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo(t.getValue(TestCase.FOO) + ": VGhpcyBpcyBhIHRlc3Qu"); + + t = TemplateFactory.HTML.get("encodeBase64"); + t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT + " URL Encoded."); + assertThat(t.getContent()).as("with URL encoding").contains("VGhpcyBpcyBhIHRlc3QuIFVSTCBFbmNvZGVkLg%3D%3D"); } @Test @@ -50,6 +54,15 @@ class TestEncode { var t = TemplateFactory.TXT.get("encodeJs"); t.setAttribute(TestCase.FOO, "'\"\\/"); assertThat(t.getContent()).isEqualTo("\\'\\\"\\\\\\/"); + + t = TemplateFactory.TXT.get("encodeJs"); + t.setAttribute(TestCase.FOO, "This is\f\b a\r\n\ttest"); + assertThat(t.getContent()).isEqualTo("This is\\f\\b a\\r\\n\\ttest"); + + t = TemplateFactory.HTML.get("encodeJs"); + t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"'); + assertThat(t.getContent()).as("with unicode") + .isEqualTo("\\u005C\\u0022\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E\\u005C\\u0022"); } @Test @@ -57,6 +70,10 @@ class TestEncode { var t = TemplateFactory.JSON.get("encodeJson"); t.setAttribute(TestCase.FOO, "This is a \"•test\""); assertThat(t.getContent()).isEqualTo("{\n \"foo\": \"This is a \\\"\\u2022test\\\"\"\n}"); + + t = TemplateFactory.HTML.get("encodeJson"); + t.setAttribute(TestCase.FOO, "\"