Compare commits

..

77 commits

Author SHA1 Message Date
ba0b5b2e9c
1.2.1-SNAPSHOT 2025-02-25 09:39:08 -08:00
013218f399
Bump bld to version 2.2.1 2025-02-25 09:38:36 -08:00
68d88ea7d3
Bump AssertJ to version 3.27.3 2025-02-25 09:37:49 -08:00
10ea21245b
Bump JUnit to version 5.12.0 2025-02-25 09:37:02 -08:00
9c14794ee8
Version 1.2.0 2025-01-14 12:50:49 -08:00
8383f75ad9
Bumped bld to version 2.2.0 2025-01-14 12:50:32 -08:00
b0f9c5a4ed
Bumped RIFE2 to version 1.9.1 2025-01-14 12:49:24 -08:00
b10826481e
Bumped JUnit to version 5.11.4 2025-01-14 12:48:54 -08:00
2c842bdb6a
Bumped AssertJ to version 3.27.2 2025-01-14 12:47:49 -08:00
a1ab01a7fc
Bumped PMD extension to version 1.1.0 2025-01-14 12:46:09 -08:00
4731afb83b
Bumped Jacoco extension to version 0.9.9 2025-01-14 12:45:47 -08:00
6dc23012cc
1.2.0 SNAPSHOT 2024-11-15 11:48:01 -08:00
99f6afe4fb
Added capitalizeWords renderer 2024-11-15 11:47:40 -08:00
daadd0bc4a
Added missing and cleaned up JavaDocs 2024-10-29 22:59:27 -07:00
97edc07160
Updated dependencies
Bumped JDK to version 23 (GitHub CI Workflow)
Bumped PMD extensions to version 1.1.7
Bumped JUnit to version 5.11.3
2024-10-29 22:39:52 -07:00
911d54c10b
Minor cleanups 2024-09-04 23:47:12 -07:00
5090b8224c
Added soft assertions 2024-09-04 23:46:13 -07:00
5cfb6fa5f8
Bumped bld to version 2.1.0 2024-09-04 23:45:51 -07:00
313121cfcc
Version 1.1.6 2024-07-31 17:17:02 -07:00
957bb694c6
Improved normalizing with support for common separators 2024-07-31 17:10:00 -07:00
c04fa16307
Bumped bld to version 2.0.1 2024-07-29 17:37:50 -07:00
429852371f
Bumped bld to version 2.0.1 2024-07-29 17:30:00 -07:00
0b24f08cb1
Added JDK 22 to CI 2024-03-21 14:00:47 -07:00
48eeada6a9 Fixed Maven Central badge 2024-02-29 20:17:20 -08:00
87f9e90d67 Version 1.1.5 2024-02-28 11:26:06 -08:00
0d14c27032 Added JS escaping for newline, linefeed, return, tab and backspace. Closes #1 2024-02-28 03:29:29 -08:00
a0ebfe2566 Bumped to bld 1.9.0 2024-02-26 13:48:58 -08:00
3f4c2abac1 Bumped bld to 1.8.0 2024-01-31 01:07:29 -08:00
cc30348981 Minor cleanup 2024-01-31 01:04:48 -08:00
4cedb3ab62 Updated copyright 2024-01-31 01:04:38 -08:00
dd614a27de Added rife2 repository badge 2023-10-22 15:53:31 -07:00
8b391830b2 Version 1.1.4 2023-10-22 15:36:50 -07:00
7e0d7ff1f6 Updated extensions 2023-10-22 15:36:34 -07:00
77beb954ce Version 1.1.3 2023-10-22 13:33:45 -07:00
eeb2965a5e Updated dependencies 2023-10-20 21:35:56 -07:00
ab59d559e4 Upgraded to bld 1.7.2 2023-08-20 19:56:05 -07:00
7bbacf7a30 Upgrade to bld 1.7.1 2023-08-18 02:15:27 -07:00
9d2a1d4324
Update bld-wrapper.properties 2023-08-17 12:52:27 -07:00
5a0dbdffed Added bld badge 2023-08-16 14:16:40 -07:00
ca65b86e1d Also publish to rife2's repo 2023-08-12 11:40:04 -07:00
079671626a Added more tests 2023-08-12 11:06:23 -07:00
a57c5afd62 Upgraded to RIFE2 1.7.0 2023-05-12 15:03:19 -07:00
1490b29da5 Fixed sonatype repos 2023-04-15 15:18:08 -07:00
85cf1ee47f Updated test-badges extension 2023-04-15 14:43:02 -07:00
623ecfa7af Added pmd extension with RIFE2 1.5.20 2023-04-15 14:16:08 -07:00
60d15e253e Minor cleanup 2023-04-15 13:53:11 -07:00
25b94487ad Updated to RIFE2 1.5.19 2023-04-10 08:20:42 -04:00
9cd94acfe4
Update publish.yml 2023-04-08 19:33:42 -04:00
b472519c23
Update publish.yml 2023-04-08 19:24:06 -04:00
452bcc3ada
Update publish.yml 2023-04-08 19:21:04 -04:00
5d7c90e25d
Update publish.yml 2023-04-08 19:18:50 -04:00
671af50999 Added signing credentials to the publish workflow 2023-04-08 14:28:40 -07:00
a415c9fd21 Added sonatype releases credentials 2023-04-08 13:51:27 -07:00
32311785ea Fixed RIFE2 releases repository credentials 2023-04-08 13:29:25 -07:00
bdffb50375 Fixed wrong credentials file location 2023-04-08 13:24:27 -07:00
e29d4ee1c7 Added RIFE2 repo credentials to the publish workflow 2023-04-08 13:18:48 -07:00
7cc5b36568 Fixed RIFE2 repository locations 2023-04-08 12:48:59 -07:00
d065aa9843 Added RIFE2 repo credentials 2023-04-08 12:29:52 -07:00
9439b18f68 Fix for publish workflow 2023-04-08 11:28:54 -07:00
35ae2b6333 Version 1.1.1 2023-04-08 11:04:49 -07:00
a24a948a61 Changed signing keys properties 2023-04-08 10:52:02 -07:00
0fa6962503 Fixed bld-ci badge 2023-04-08 10:32:41 -07:00
630b57b604 Moved from Gradle to bld 2023-04-08 10:28:20 -07:00
5df56b7820 Version 1.1.0 2023-03-26 12:01:07 -07:00
b77013b6cc Added NO_CI env to GITHUB_ENV 2023-03-26 00:09:56 -07:00
6f5219761b Fix for NO_CI step 2023-03-25 23:22:02 -07:00
20f8aef5c6 Prevented some tests from being repeated on CI 2023-03-25 23:08:08 -07:00
0ca9f5b9f8 Added error logging to fetchUrl 2023-03-25 20:50:03 -07:00
4bba966770 Switched to generic properties parsing on more renderers 2023-03-25 20:49:30 -07:00
c1b0e7fcf5 Javadocs 2023-03-25 15:50:06 -07:00
9738ecb0ba Improved the renderers encoding by using the template encoding or encoding specified in a property 2023-03-25 15:46:05 -07:00
538a8d35ae Switched to use StringUtils for un/captitalize 2023-03-24 09:22:30 -07:00
79cbf76761 Added validateCreditMethod standalone method 2023-03-23 20:37:01 -07:00
f264bb43bc Minor cleanup 2023-03-22 15:05:39 -07:00
a5855e6638 Temporary fix for the Maven Central badge 2023-03-22 14:34:58 -07:00
b554fc7f58
Fixed Maven Central badge 2023-03-22 12:36:33 -07:00
3bc761995f
Update README.md
Fixed badges
2023-03-22 11:55:52 -07:00
116 changed files with 1609 additions and 1222 deletions

32
.github/workflows/bld.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: bld-ci
on: [push, pull_request, workflow_dispatch]
jobs:
build-bld-project:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [17, 21, 23]
steps:
- name: Checkout source repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
- name: Download dependencies
run: ./bld download
- name: Run tests
run: >-
./bld compile test
-DtestsBadgeUrl=https://rife2.com/tests-badge/update/com.uwyn.rife2/rife2-renderers
-DtestsBadgeApiKey=${{ secrets.TESTS_BADGE_API_KEY }}

View file

@ -1,35 +0,0 @@
name: gradle-ci
on: [ push, pull_request, workflow_dispatch ]
jobs:
build-gradle-project:
runs-on: ubuntu-latest
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
strategy:
matrix:
java-version: [ 17, 19 ]
steps:
- name: Checkout source repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: ${{ matrix.java-version }}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Test with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build check --stacktrace -PtestsBadgeApiKey=${{ secrets.TESTS_BADGE_API_KEY }}

View file

@ -28,38 +28,30 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
steps: steps:
- name: Checkout source repository - name: Checkout source repository
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 19 - name: Set up JDK 17
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: "zulu"
java-version: 19 java-version: 17
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build Javadocs - name: Build Javadocs
uses: gradle/gradle-build-action@v2 run: ./bld download clean javadoc
with:
arguments: clean javadoc
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v3 uses: actions/configure-pages@v3
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v1 uses: actions/upload-pages-artifact@v3
with: with:
# Upload generated Javadocs repository # Upload generated Javadocs repository
path: 'lib/build/docs/javadoc/' path: "build/javadoc/"
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v1 uses: actions/deploy-pages@v4

View file

@ -1,4 +1,4 @@
name: Publish to the Maven Central name: Publish to the Maven Central/Rife2
on: on:
workflow_dispatch: workflow_dispatch:
@ -6,34 +6,38 @@ on:
types: [released] types: [released]
jobs: jobs:
build-gradle-project: build-bld-project:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
steps: steps:
- name: Checkout source repository - name: Checkout source repository
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: "temurin"
java-version: 17 java-version: 17
- name: Grant execute permission for gradlew - name: Download dependencies
run: chmod +x gradlew run: ./bld download
- name: Publish with Gradle - name: Import key
uses: gradle/gradle-build-action@v2 run: echo "${{ secrets.SIGN_SECRET_KEY }}" | gpg --batch --import
env:
SONATYPE_USER: ${{ secrets.SONATYPE_USER}}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_signingKey }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingPassword }}
with:
arguments: build check publish --stacktrace -PtestsBadgeApiKey=${{ secrets.TESTS_BADGE_API_KEY }}
- name: Publish
run: >-
./bld compile test publish
-DtestsBadgeUrl=https://rife2.com/tests-badge/update/com.uwyn.rife2/rife2-renderers
-DtestsBadgeApiKey=${{ secrets.TESTS_BADGE_API_KEY }}
-Drife2Username=${{ secrets.RIFE2_USERNAME }}
-Drife2Password=${{ secrets.RIFE2_PASSWORD }}
-DsonatypeUser=${{ secrets.SONATYPE_USER }}
-DsonatypePassword=${{ secrets.SONATYPE_PASSWORD }}
-DsignKey=${{ secrets.SIGN_KEY }}
-DsignPassphrase=${{ secrets.SIGN_PASSPHRASE }}
- name: Delete GnuPG data
run: rm -rfv $HOME/.gnupg

135
.gitignore vendored
View file

@ -1,84 +1,55 @@
!.vscode/extensions.json
!.vscode/launch.json
!.vscode/settings.json
!.vscode/tasks.json
*.class
*.code-workspace
*.ctxt
*.iws
*.log
*.nar
*.rar
*.sublime-*
*.tar.gz
*.zip
.DS_Store
.classpath
.gradle .gradle
.history .DS_Store
.kobalt build
.mtj.tmp/ lib/bld/**
.mvn/timing.properties lib/compile/**
.mvn/wrapper/maven-wrapper.jar lib/runtime/**
.nb-gradle lib/standalone/**
.project lib/test/**
.scannerwork !bld-wrapper.jar
.settings !bld-wrapper.properties
.vscode/*
/**/.idea/$CACHE_FILE$ # IDEA ignores
/**/.idea/$PRODUCT_WORKSPACE_FILE$
/**/.idea/**/caches/build_file_checksums.ser # User-specific
/**/.idea/**/contentModel.xml .idea/**/workspace.xml
/**/.idea/**/dataSources.ids .idea/**/tasks.xml
/**/.idea/**/dataSources.local.xml .idea/**/usage.statistics.xml
/**/.idea/**/dataSources/ .idea/**/dictionaries
/**/.idea/**/dbnavigator.xml .idea/**/shelf
/**/.idea/**/dictionaries
/**/.idea/**/dynamic.xml # AWS User-specific
/**/.idea/**/gradle.xml .idea/**/aws.xml
/**/.idea/**/httpRequests
/**/.idea/**/libraries # Generated files
/**/.idea/**/mongoSettings.xml .idea/**/contentModel.xml
/**/.idea/**/replstate.xml
/**/.idea/**/shelf/ # Sensitive or high-churn files
/**/.idea/**/sqlDataSources.xml .idea/**/dataSources/
/**/.idea/**/tasks.xml .idea/**/dataSources.ids
/**/.idea/**/uiDesigner.xml .idea/**/dataSources.local.xml
/**/.idea/**/usage.statistics.xml .idea/**/sqlDataSources.xml
/**/.idea/**/workspace.xml .idea/**/dynamic.xml
/**/.idea/sonarlint* .idea/**/uiDesigner.xml
/**/.idea_modules/ .idea/**/dbnavigator.xml
Thumbs.db
__pycache__ # Gradle
.idea/**/gradle.xml
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml atlassian-ide-plugin.xml
bin/
build/ # Cursive Clojure plugin
cmake-build-*/ .idea/replstate.xml
com_crashlytics_export_strings.xml
crashlytics-build.properties # SonarLint plugin
crashlytics.properties .idea/sonarlint/
dependency-reduced-pom.xml
deploy/ # Editor-based Rest Client
dist/ .idea/httpRequests
ehthumbs.db
fabric.properties
gen/
gradle.properties
hs_err_pid*
kobaltBuild
kobaltw*-test
lib/kotlin*
libs/
local.properties
out/
pom.xml.asc
pom.xml.next
pom.xml.releaseBackup
pom.xml.tag
pom.xml.versionsBackup
proguard-project.txt
project.properties
release.properties
target/
test-output
venv

3
.idea/.gitignore generated vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

37
.idea/app.iml generated Normal file
View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/main" />
<output-test url="file://$MODULE_DIR$/build/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/src/main/resources/templates" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="RUNTIME">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/src/main/resources/templates" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="compile" level="project" />
<orderEntry type="library" scope="RUNTIME" name="runtime" level="project" />
<orderEntry type="library" scope="TEST" name="test" level="project" />
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/src/test/resources/templates" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

14
.idea/bld.iml generated Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/bld" />
<output-test url="file://$MODULE_DIR$/build/bld" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/bld">
<sourceFolder url="file://$MODULE_DIR$/src/bld/java" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="bld" level="project" />
</component>
</module>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="BldConfiguration">
<bytecodeTargetLevel target="17" /> <events />
</component> </component>
</project> </project>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
</state>
</component>

View file

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value=" Copyright &amp;#36;today.year the original author or authors.&#10; &#10; Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10; you may not use this file except in compliance with the License.&#10; You may obtain a copy of the License at&#10; &#10; https://www.apache.org/licenses/LICENSE-2.0&#10; &#10; Unless required by applicable law or agreed to in writing, software&#10; distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10; See the License for the specific language governing permissions and&#10; limitations under the License.&#10; " /> <option name="notice" value=" Copyright 2023-&amp;#36;today.year the original author or authors.&#10; &#10; Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10; you may not use this file except in compliance with the License.&#10; You may obtain a copy of the License at&#10; &#10; https://www.apache.org/licenses/LICENSE-2.0&#10; &#10; Unless required by applicable law or agreed to in writing, software&#10; distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10; See the License for the specific language governing permissions and&#10; limitations under the License.&#10; " />
<option name="myName" value="Apache License" /> <option name="myName" value="Apache License" />
</copyright> </copyright>
</component> </component>

12
.idea/icon.svg generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with Vectornator (http://vectornator.io/) -->
<svg height="100%" stroke-miterlimit="10" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" version="1.1" viewBox="0 0 50 50" width="100%" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs/>
<g id="Untitled">
<g opacity="1">
<path d="M31.9421 4.99551C26.8094 7.90585 24.7641 12.0654 24.2025 17.855C23.7876 22.1321 27.3936 27.0983 27.5933 29.9084C27.8096 32.9493 26.063 35.2557 22.623 35.8686C20.4949 36.2477 17.8742 36.0139 14.6092 33.2624C14.6678 38.0258 16.7542 42.433 21.5076 44.1302C26.4211 45.8835 31.2671 44.9143 34.6016 41.9865C39.1593 38.2111 40.2692 32.4953 36.786 26.6511C33.0398 20.3619 29.6701 15.5562 31.9421 4.99551" fill="#fa9052" fill-rule="nonzero" opacity="1" stroke="none"/>
<path d="M17.349 14.5478C17.3497 14.5395 17.349 14.5478 17.349 14.5478C17.1626 14.8365 16.9754 15.1245 16.7869 15.4119C15.8906 16.7783 14.949 18.1004 13.9706 19.4087C12.6688 21.1498 10.9828 23.3556 11.1816 25.6096C11.3531 26.7195 12.0143 27.6556 12.9129 28.3061C13.6009 28.7369 14.38 28.9871 15.1883 29.0868C16.1147 29.2013 17.0209 29.1429 17.9083 28.834C19.257 28.1671 19.8037 27.4604 20.0005 26.4317C20.0649 26.1834 20.0437 25.8312 20.0437 25.8312C20.0343 25.73 20.0272 25.6537 20.0152 25.5528C19.9908 25.3475 19.9566 25.1434 19.9147 24.9411C19.8678 24.7142 19.8113 24.4896 19.7476 24.2671C19.7361 24.2271 19.6483 23.9399 19.6927 24.0786C19.6573 23.9677 19.6198 23.8574 19.5812 23.7477C19.4341 23.3294 19.2669 22.9183 19.0904 22.5114C19.0904 22.5114 18.2023 20.4746 17.8867 19.4599C17.3927 17.8737 17.1809 16.2048 17.349 14.5478Z" fill="#2392ff" fill-rule="nonzero" opacity="1" stroke="none"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

204
.idea/intellij-javadocs-4.0.1.xml generated Normal file
View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaDocConfiguration">
<GENERAL>
<MODE>UPDATE</MODE>
<OVERRIDDEN_METHODS>false</OVERRIDDEN_METHODS>
<SPLITTED_CLASS_NAME>true</SPLITTED_CLASS_NAME>
<LEVELS>
<LEVEL>TYPE</LEVEL>
<LEVEL>FIELD</LEVEL>
<LEVEL>METHOD</LEVEL>
</LEVELS>
<VISIBILITIES>
<VISIBILITY>DEFAULT</VISIBILITY>
<VISIBILITY>PROTECTED</VISIBILITY>
<VISIBILITY>PUBLIC</VISIBILITY>
</VISIBILITIES>
</GENERAL>
<TEMPLATES>
<CLASSES>
<CLASS>
<KEY>^.*(public|protected|private)*.+interface\s+\w+.*</KEY>
<VALUE>/**\n
* The interface ${name}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
*/</VALUE>
</CLASS>
<CLASS>
<KEY>^.*(public|protected|private)*.+enum\s+\w+.*</KEY>
<VALUE>/**\n
* The enum ${name}.\n
*/</VALUE>
</CLASS>
<CLASS>
<KEY>^.*(public|protected|private)*.+class\s+\w+.*</KEY>
<VALUE>/**\n
* The type ${name}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
*/</VALUE>
</CLASS>
<CLASS>
<KEY>.+</KEY>
<VALUE>/**\n
* The type ${name}.\n
*/</VALUE>
</CLASS>
</CLASSES>
<CONSTRUCTORS>
<CONSTRUCTOR>
<KEY>.+</KEY>
<VALUE>/**\n
* Instantiates a new ${name}.\n
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</CONSTRUCTOR>
</CONSTRUCTORS>
<METHODS>
<METHOD>
<KEY>^.*(public|protected|private)*\s*.*(\w(\s*&lt;.+&gt;)*)+\s+get\w+\s*\(.*\).+</KEY>
<VALUE>/**\n
* Gets ${partName}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${partName}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>^.*(public|protected|private)*\s*.*(void|\w(\s*&lt;.+&gt;)*)+\s+set\w+\s*\(.*\).+</KEY>
<VALUE>/**\n
* Sets ${partName}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${partName}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+</KEY>
<VALUE>/**\n
* The entry point of application.\n
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
* @param ${element.parameterList.parameters[0].name} the input arguments\n
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>.+</KEY>
<VALUE>/**\n
* ${name}&lt;#if isNotVoid&gt; ${return}&lt;/#if&gt;.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${return}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
</METHODS>
<FIELDS>
<FIELD>
<KEY>^.*(public|protected|private)*.+static.*(\w\s\w)+.+</KEY>
<VALUE>/**\n
* The constant ${element.getName()}.\n
*/</VALUE>
</FIELD>
<FIELD>
<KEY>^.*(public|protected|private)*.*(\w\s\w)+.+</KEY>
<VALUE>/**\n
&lt;#if element.parent.isInterface()&gt;
* The constant ${element.getName()}.\n
&lt;#else&gt;
* The ${name}.\n
&lt;/#if&gt; */</VALUE>
</FIELD>
<FIELD>
<KEY>.+</KEY>
<VALUE>/**\n
&lt;#if element.parent.isEnum()&gt;
*${name} ${typeName}.\n
&lt;#else&gt;
* The ${name}.\n
&lt;/#if&gt;*/</VALUE>
</FIELD>
</FIELDS>
</TEMPLATES>
</component>
</project>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://s01.oss.sonatype.org/content/repositories/snapshots" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:$PROJECT_DIR$/../../maven/repository/" />
</remote-repository>
</component>
</project>

18
.idea/libraries/bld.xml generated Normal file
View file

@ -0,0 +1,18 @@
<component name="libraryTable">
<library name="bld">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/bld" />
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/bld" />
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1-sources.jar!/" />
</SOURCES>
<excluded>
<root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" />
</excluded>
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" />
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" type="SOURCES" />
</library>
</component>

13
.idea/libraries/compile.xml generated Normal file
View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="compile">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" type="SOURCES" />
</library>
</component>

13
.idea/libraries/runtime.xml generated Normal file
View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="runtime">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" type="SOURCES" />
</library>
</component>

13
.idea/libraries/test.xml generated Normal file
View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="test">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/test" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/test" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" type="SOURCES" />
</library>
</component>

64
.idea/misc.xml generated
View file

@ -1,57 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="EntryPointsManager"> <component name="EntryPointsManager">
<pattern value="rife2.render.Capitalize" /> <pattern value="rife.render.TemplateRenderersBuild" method="jacoco" />
<pattern value="rife2.render.Lowercase" /> <pattern value="rife.render.TemplateRenderersBuild" method="pmd" />
<pattern value="rife2.render.SwapCase" />
<pattern value="rife2.render.Trim" />
<pattern value="rife2.render.Uncapitalize" />
<pattern value="rife2.render.Uppercase" />
<pattern value="rife2.render.EncodeHtml" />
<pattern value="rife2.render.EncodeJson" />
<pattern value="rife2.render.EncodeUrl" />
<pattern value="rife2.render.EncodeXml" />
<pattern value="rife2.render.EncodeQp" />
<pattern value="rife2.render.EncodeHtmlEntities" />
<pattern value="rife2.render.Rot13" />
<pattern value="rife2.render.Year" />
<pattern value="rife2.render.EncodeBase64" />
<pattern value="rife2.render.EncodeUnicode" />
<pattern value="rife.render.Capitalize" />
<pattern value="rife.render.EncodeBase64" />
<pattern value="rife.render.EncodeHtml" />
<pattern value="rife.render.EncodeHtmlEntities" />
<pattern value="rife.render.EncodeJson" />
<pattern value="rife.render.EncodeQp" />
<pattern value="rife.render.EncodeUnicode" />
<pattern value="rife.render.EncodeUrl" />
<pattern value="rife.render.EncodeXml" />
<pattern value="rife.render.ValueBean" method="getValue" />
<pattern value="rife.render.Lowercase" />
<pattern value="rife.render.Rot13" />
<pattern value="rife.render.SwapCase" />
<pattern value="rife.render.TestDateTime" />
<pattern value="rife.render.TestEncode" />
<pattern value="rife.render.Trim" />
<pattern value="rife.render.Uncapitalize" />
<pattern value="rife.render.Uppercase" />
<pattern value="rife.render.Year" />
<pattern value="rife.render.ValueBean" method="setValue" /> <pattern value="rife.render.ValueBean" method="setValue" />
<pattern value="rife.render.TestDateTime" method="testDateIso" />
<pattern value="rife.render.TestDateTime" method="testDateTimeIso" />
<pattern value="rife.render.TestDateTime" method="testDateTimeRfc2822" />
<pattern value="rife.render.TestEncode" method="testEncodeUrl" />
<pattern value="rife.render.TestDateTime" method="testTimeIso" />
<pattern value="rife.render.TestDateTime" method="testYear" />
<pattern value="rife.render.ValueBean" />
<pattern value="rife.render.RenderUtils" method="formatCreditCard" />
<pattern value="rife.render.RenderUtils" />
<pattern value="rife.render.RenderUtils" method="RenderUtils" />
<pattern value="rife.render.RenderUtils" method="toQuotedPrintable" />
</component> </component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="PDMPlugin"> <component name="PDMPlugin">
<option name="customRuleSets">
<list>
<option value="K:\java\semver\config\pmd.xml" />
<option value="$PROJECT_DIR$/../bld-jacoco-report/config/pmd.xml" />
<option value="$PROJECT_DIR$/../bld-checkstyle/config/pmd.xml" />
<option value="$PROJECT_DIR$/../bld-exec/config/pmd.xml" />
<option value="$PROJECT_DIR$/../bld-testng/config/pmd.xml" />
<option value="$PROJECT_DIR$/../bld-generated-version/config/pmd.xml" />
<option value="$PROJECT_DIR$/../bld-pitest/config/pmd.xml" />
</list>
</option>
<option name="skipTestSources" value="false" /> <option name="skipTestSources" value="false" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="19" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build" />
</component>
</project> </project>

9
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/app.iml" filepath="$PROJECT_DIR$/.idea/app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/bld.iml" filepath="$PROJECT_DIR$/.idea/bld.iml" />
</modules>
</component>
</project>

9
.idea/runConfigurations/Run Main.xml generated Normal file
View file

@ -0,0 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Main" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="rife.render.TemplateRenderersMain" />
<module name="app" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

13
.idea/runConfigurations/Run Tests.xml generated Normal file
View file

@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Tests" type="JUnit" factoryName="JUnit">
<module name="app" />
<option name="PACKAGE_NAME" value="moog" />
<option name="MAIN_CLASS_NAME" value="rife.render" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="directory" />
<dir value="$PROJECT_DIR$/src/test/java" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

24
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Run Main",
"request": "launch",
"mainClass": "rife.render.TemplateRenderersMain"
},
{
"type": "java",
"name": "Run Tests",
"request": "launch",
"mainClass": "org.junit.platform.console.ConsoleLauncher",
"args": [
"--details=verbose",
"--scan-classpath",
"--disable-banner",
"--disable-ansi-colors",
"--exclude-engine=junit-platform-suite",
"--exclude-engine=junit-vintage"]
}
]
}

15
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,15 @@
{
"java.project.sourcePaths": [
"src/main/java",
"src/main/resources",
"src/test/java",
"src/test/resources",
"src/bld/java",
"src/bld/resources"
],
"java.configuration.updateBuildConfiguration": "automatic",
"java.project.referencedLibraries": [
"${HOME}/.bld/dist/bld-2.2.1.jar",
"lib/**/*.jar"
]
}

View file

@ -1,9 +1,11 @@
[![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Java](https://img.shields.io/badge/java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) [![Java](https://img.shields.io/badge/java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
[![Release](https://img.shields.io/github/release/rife2-template-renderers/rife2.svg)](https://github.com/rife2/rife2-template-renderers/releases/latest) [![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.uwyn.rife2/rife2-renderers/badge.svg?color=blue)](https://maven-badges.herokuapp.com/maven-central/com.uwyn.rife2/rife2-renderers) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/rife2/rife2-template-renderers)](https://github.com/rife2/rife2-template-renderers/releases/latest)
[![Release](https://flat.badgen.net/maven/v/metadata-url/repo.rife2.com/releases/com/uwyn/rife2/rife2-renderers/maven-metadata.xml?color=blue)](https://repo.rife2.com/#/releases/com/uwyn/rife2/rife2-renderers)
[![Maven Central](https://img.shields.io/maven-central/v/com.uwyn.rife2/rife2-renderers)](https://central.sonatype.com/artifact/com.uwyn.rife2/rife2-renderers/)
[![Nexus Snapshot](https://img.shields.io/nexus/s/com.uwyn.rife2/rife2-renderers?server=https%3A%2F%2Fs01.oss.sonatype.org%2F)](https://s01.oss.sonatype.org/content/repositories/snapshots/com/uwyn/rife2/rife2-renderers/) [![Nexus Snapshot](https://img.shields.io/nexus/s/com.uwyn.rife2/rife2-renderers?server=https%3A%2F%2Fs01.oss.sonatype.org%2F)](https://s01.oss.sonatype.org/content/repositories/snapshots/com/uwyn/rife2/rife2-renderers/)
[![GitHub CI](https://github.com/rife2/rife2-template-renderers/actions/workflows/gradle.yml/badge.svg)](https://github.com/rife2/rife2-template-renderers/actions/workflows/gradle.yml) [![GitHub CI](https://github.com/rife2/rife2-template-renderers/actions/workflows/bld.yml/badge.svg)](https://github.com/rife2/rife2-template-renderers/actions/workflows/bld.yml)
[![Tests](https://rife2.com/tests-badge/badge/com.uwyn.rife2/rife2-renderers)](https://github.com/rife2/rife2-template-renderers/actions/workflows/gradle.yml) [![Tests](https://rife2.com/tests-badge/badge/com.uwyn.rife2/rife2-renderers)](https://github.com/rife2/rife2-template-renderers/actions/workflows/gradle.yml)
# [RIFE2](https://rife2.com/) Template Renderers # [RIFE2](https://rife2.com/) Template Renderers
@ -24,7 +26,7 @@ This project provides a collection of useful template renderers.
## Encoding Renderers ## Encoding Renderers
| Renderer | Description | | Renderer | Description |
|:--------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------| |:------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------|
| [rife.render.EncodeBase64](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeBase64) | Encodes a template value to Base64 | | [rife.render.EncodeBase64](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeBase64) | Encodes a template value to Base64 |
| [rife.render.EncodeHtml](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeHtml) | Encodes a template value to HTML | | [rife.render.EncodeHtml](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeHtml) | Encodes a template value to HTML |
| [rife.render.EncodeHtmlEntities](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeHtmlEntities) | Encodes a template value to HTML decimal entities | | [rife.render.EncodeHtmlEntities](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.EncodeHtmlEntities) | Encodes a template value to HTML decimal entities |
@ -50,15 +52,16 @@ This project provides a collection of useful template renderers.
## Text Renderers ## Text Renderers
| Renderer | Description | | Renderer | Description |
|:------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------| |:------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------|
| [rife.render.Capitalize](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Capitalize) | Capitalizes a template value | | [rife.render.Capitalize](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Capitalize) | Capitalizes a template value |
| [rife.render.CapitalizeWords](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.CapitalizeWords) | Capitalizes words of a template value |
| [rife.render.Lowercase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Lowercase) | Converts a template value to lowercase | | [rife.render.Lowercase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Lowercase) | Converts a template value to lowercase |
| [rife.render.Rot13](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Rot13) | Translates a template value to/from ROT13 | | [rife.render.Rot13](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Rot13) | Translates a template value to/from ROT13 |
| [rife.render.SwapCase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.SwapCase) | Swap case of a template value | | [rife.render.SwapCase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.SwapCase) | Swaps case of a template value |
| [rife.render.Trim](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Trim) | Removes leading and trailing whitespace from a template value | | [rife.render.Trim](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Trim) | Removes leading and trailing whitespace from a template value |
| [rife.render.Uncapitalize](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Uncapitalize) | Uncapitalizes a template value | | [rife.render.Uncapitalize](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Uncapitalize) | Uncapitalizes a template value |
| [rife.render.Uppercase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Uppercase) | Converts a template value to uppercase | | [rife.render.Uppercase](https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Uppercase) | Converts a template value to uppercase |
## Documentation ## Documentation
Read more in the [documenation](https://github.com/rife2/rife2-template-renderers/wiki). Read more in the [documentation](https://github.com/rife2/rife2-template-renderers/wiki).

2
bld Executable file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env sh
java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build rife.render.TemplateRenderersBuild "$@"

4
bld.bat Normal file
View file

@ -0,0 +1,4 @@
@echo off
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build rife.render.TemplateRenderersBuild %*

View file

@ -7,8 +7,9 @@
<!-- BEST PRACTICES --> <!-- BEST PRACTICES -->
<rule ref="category/java/bestpractices.xml"> <rule ref="category/java/bestpractices.xml">
<exclude name="AvoidPrintStackTrace"/> <exclude name="AvoidPrintStackTrace"/>
<exclude name="JUnit4TestShouldUseTestAnnotation"/> <exclude name="GuardLogStatement"/>
<exclude name="JUnitTestContainsTooManyAsserts"/> <exclude name="UnitTestContainsTooManyAsserts"/>
<exclude name="UnitTestShouldUseTestAnnotation"/>
</rule> </rule>
<rule ref="category/java/bestpractices.xml/MissingOverride"> <rule ref="category/java/bestpractices.xml/MissingOverride">
@ -25,7 +26,6 @@
<exclude name="ClassNamingConventions"/> <exclude name="ClassNamingConventions"/>
<exclude name="CommentDefaultAccessModifier"/> <exclude name="CommentDefaultAccessModifier"/>
<exclude name="ConfusingTernary"/> <exclude name="ConfusingTernary"/>
<exclude name="DefaultPackage"/>
<exclude name="FieldNamingConventions"/> <exclude name="FieldNamingConventions"/>
<exclude name="LocalVariableCouldBeFinal"/> <exclude name="LocalVariableCouldBeFinal"/>
<exclude name="LongVariable"/> <exclude name="LongVariable"/>
@ -35,8 +35,15 @@
<exclude name="ShortClassName"/> <exclude name="ShortClassName"/>
<exclude name="ShortMethodName"/> <exclude name="ShortMethodName"/>
<exclude name="ShortVariable"/> <exclude name="ShortVariable"/>
<exclude name="UselessParentheses"/> <exclude name="UseExplicitTypes"/>
<exclude name="UseUnderscoresInNumericLiterals"/> <exclude name="UseUnderscoresInNumericLiterals"/>
<exclude name="UselessParentheses"/>
</rule>
<rule ref="category/java/codestyle.xml/UnnecessaryImport">
<properties>
<property name="violationSuppressRegex" value="Unused (static|.*\.\*).*"/>
</properties>
</rule> </rule>
<!-- DESIGN --> <!-- DESIGN -->
@ -46,8 +53,6 @@
<exclude name="AvoidUncheckedExceptionsInSignatures"/> <exclude name="AvoidUncheckedExceptionsInSignatures"/>
<exclude name="CognitiveComplexity"/> <exclude name="CognitiveComplexity"/>
<exclude name="CyclomaticComplexity"/> <exclude name="CyclomaticComplexity"/>
<exclude name="ExcessiveClassLength"/>
<exclude name="ExcessiveMethodLength"/>
<exclude name="ExcessiveParameterList"/> <exclude name="ExcessiveParameterList"/>
<exclude name="ExcessivePublicCount"/> <exclude name="ExcessivePublicCount"/>
<exclude name="GodClass"/> <exclude name="GodClass"/>
@ -74,7 +79,6 @@
<exclude name="AvoidFieldNameMatchingMethodName"/> <exclude name="AvoidFieldNameMatchingMethodName"/>
<exclude name="AvoidFieldNameMatchingTypeName"/> <exclude name="AvoidFieldNameMatchingTypeName"/>
<exclude name="AvoidLiteralsInIfCondition"/> <exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="EmptyCatchBlock"/>
<exclude name="NullAssignment"/> <exclude name="NullAssignment"/>
</rule> </rule>
@ -90,13 +94,6 @@
<property name="skipAnnotations" value="true"/> <property name="skipAnnotations" value="true"/>
</properties> </properties>
</rule> </rule>
<rule ref="category/java/errorprone.xml/EmptyCatchBlock">
<properties>
<property name="allowExceptionNameRegex">
<value>^ignore$</value>
</property>
</properties>
</rule>
<!-- MULTITHREADING --> <!-- MULTITHREADING -->
<rule ref="category/java/multithreading.xml"> <rule ref="category/java/multithreading.xml">

Binary file not shown.

View file

@ -1,6 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

244
gradlew vendored
View file

@ -1,244 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original 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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored
View file

@ -1,92 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
lib/bld/bld-wrapper.jar Normal file

Binary file not shown.

View file

@ -0,0 +1,8 @@
bld.downloadExtensionJavadoc=false
bld.downloadExtensionSources=true
bld.downloadLocation=
bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9
bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.0
bld.extension-tests=com.uwyn.rife2:bld-tests-badge:1.4.6
bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
bld.version=2.2.1

View file

@ -1,191 +0,0 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
plugins {
`java-library`
`maven-publish`
pmd
signing
id("com.github.ben-manes.versions") version "0.46.0"
}
val rifeVersion by rootProject.extra { "1.5.4" }
group = "com.uwyn.rife2"
version = "1.0.0"
repositories {
mavenLocal()
mavenCentral()
maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT
}
dependencies {
implementation("com.uwyn.rife2:rife2:${rifeVersion}") {
this.isChanging = true
}
runtimeOnly("com.uwyn.rife2:rife2:${rifeVersion}:agent") {
this.isChanging = true
}
testImplementation(platform("org.junit:junit-bom:5.9.2"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.assertj:assertj-core:3.24.2")
}
configurations {
all {
resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
}
}
java {
withJavadocJar()
withSourcesJar()
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
pmd {
isIgnoreFailures = true
ruleSetFiles = files("${projectDir}/config/pmd.xml")
isConsoleOutput = true
}
tasks {
withType<JavaCompile> {
options.encoding = "UTF-8"
}
test {
val apiKey = project.properties["testsBadgeApiKey"]
useJUnitPlatform()
testLogging {
exceptionFormat = TestExceptionFormat.FULL
events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
}
addTestListener(object : TestListener {
override fun beforeTest(p0: TestDescriptor?) = Unit
override fun beforeSuite(p0: TestDescriptor?) = Unit
override fun afterTest(desc: TestDescriptor, result: TestResult) = Unit
override fun afterSuite(desc: TestDescriptor, result: TestResult) {
if (desc.parent != null) {
val output = result.run {
"Results: $resultType (" +
"$testCount tests, " +
"$successfulTestCount successes, " +
"$failedTestCount failures, " +
"$skippedTestCount skipped" +
")"
}
val testResultLine = "| $output |"
val repeatLength = testResultLine.length
val separationLine = "-".repeat(repeatLength)
println()
println(separationLine)
println(testResultLine)
println(separationLine)
}
if (desc.parent == null) {
val passed = result.successfulTestCount
val failed = result.failedTestCount
val skipped = result.skippedTestCount
if (apiKey != null) {
val response: HttpResponse<String> = HttpClient.newHttpClient()
.send(
HttpRequest.newBuilder()
.uri(
URI(
"https://rife2.com/tests-badge/update/com.uwyn.rife2/rife2-renderers?" +
"apiKey=$apiKey&" +
"passed=$passed&" +
"failed=$failed&" +
"skipped=$skipped"
)
)
.POST(HttpRequest.BodyPublishers.noBody())
.build(), HttpResponse.BodyHandlers.ofString()
)
println("RESPONSE: " + response.statusCode())
println(response.body())
}
}
}
})
}
javadoc {
title = "<a href=\"https://rife2.com\">RIFE2</a> Template Renderers"
options {
this as StandardJavadocDocletOptions
keyWords(true)
splitIndex(true)
links("https://rife2.github.io/rife2/")
}
}
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
artifactId = "rife2-renderers"
from(components["java"])
pom {
name.set("RIFE2 Template Renderers")
description.set("Template Renderers for the RIFE2 web framework")
url.set("https://github.com/rife2/rife2-template-renderers")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("ethauvin")
name.set("Erik C. Thauvin")
email.set("erik@thauvin.net")
url.set("https://erik.thauvin.net/")
}
developer {
id.set("gbevin")
name.set("Geert Bevin")
email.set("gbevin@uwyn.com")
url.set("https://github.com/gbevin")
}
}
scm {
connection.set("scm:git:https://github.com/rife2/rife2-template-renderers.git")
developerConnection.set("scm:git:git@github.com:rife2/rife2-template-renderers.git")
url.set("https://github.com/rife2/rife2-template-renderers")
}
}
repositories {
maven {
credentials {
username = System.getenv("SONATYPE_USER")
password = System.getenv("SONATYPE_PASSWORD")
}
val releasesRepoUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
val snapshotsRepoUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
}
}
}
}
}
signing {
val signingKey: String? by project // ORG_GRADLE_PROJECT_signingKey
val signingPassword: String? by project // ORG_GRADLE_PROJECT_signingPassword
useInMemoryPgpKeys(signingKey, signingPassword)
sign(publishing.publications["mavenJava"])
}

View file

@ -1,64 +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 rife.template.Template;
import rife.template.ValueRenderer;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
/**
* <p>Masks characters of a template value.</p>
*
* <p>Usage:</p>
*
* <pre>
* &lt;!--v render:rife.render.Mask:valueId/--&gt;
* {{v render:rife.render.Mask:valueId/}}
* </pre>
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @see <a href="https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Mask">rife.render.Mask</a>
* @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);
}
}

View file

@ -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("&#77;&#246;&#99;&#104;&#116;&#101;&#110;&#32;&#83;&#105;&#101;&#32;&#101;&#105;&#110;&#32;&#112;&#97;&#97;&#114;&#32;&#196;&#112;&#102;&#101;&#108;&#63;");
}
@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, "&bull;", -1, false)).as("mask=&bull;")
.isEqualTo("&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;");
}
@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("");
}
}

View file

@ -1,2 +0,0 @@
rootProject.name = "rife2-template-renderers"
include("lib")

View file

@ -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("<a href=\"https://rife2.com\">RIFE2</a> 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));
}
}

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
/** /**
* <p>Abbreviate a template value with ellipses.</p> * <p>Abbreviates a template value with ellipses.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -40,7 +36,18 @@ import java.util.Properties;
*/ */
public class Abbreviate implements ValueRenderer { public class Abbreviate implements ValueRenderer {
/** /**
* {@inheritDoc} * <p>Returns the template value abbreviated with ellipses.</p>
*
* <p>Two parameters can be specified:</p>
* <ul>
* <li><code>mark</code>: the string that will be used to abbreviate the value. Default is <code>...</code></li>
* <li><code>max</code>: the maximum number of characters to render. Default is <code>-1</code> (no abbreviation).</li>
* </ul>
*
* @param template the template that contains the value
* @param valueId the id of the value to render
* @param differentiator a generic string that can be used to differentiate the rendering
* @return the abbreviated value, or the original value if no abbreviation is necessary
*/ */
@Override @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
@ -48,15 +55,12 @@ public class Abbreviate implements ValueRenderer {
var max = -1; var max = -1;
var defaultValue = template.getDefaultValue(valueId); var defaultValue = template.getDefaultValue(valueId);
if (defaultValue != null) { if (defaultValue != null) {
var properties = new Properties(); var properties = RenderUtils.parsePropertiesString(defaultValue);
try {
properties.load(new StringReader(defaultValue));
mark = properties.getProperty("mark", mark); mark = properties.getProperty("mark", mark);
max = Integer.parseInt(properties.getProperty("max", String.valueOf(max))); max = Integer.parseInt(properties.getProperty("max", String.valueOf(max)));
} catch (IOException | NumberFormatException ignore) {
// do nothing
} }
}
return RenderUtils.abbreviate(template.getValueOrAttribute(differentiator), max, mark); return template.getEncoder().encode(
RenderUtils.abbreviate(template.getValueOrAttribute(differentiator), max, mark));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,10 +38,15 @@ import java.time.ZonedDateTime;
*/ */
public class BeatTime implements ValueRenderer { public class BeatTime implements ValueRenderer {
/** /**
* {@inheritDoc} * Returns the current time in Swatch Internet (.beat) Time format.
*
* @param template the template instance
* @param valueId the value id
* @param differentiator the differentiator
* @return the rendered value
*/ */
@Override @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.beatTime(ZonedDateTime.now()); return template.getEncoder().encode(RenderUtils.beatTime(ZonedDateTime.now()));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import rife.tools.StringUtils;
/** /**
* <p>Capitalizes a template value.</p> * <p>Capitalizes a template value.</p>
@ -36,10 +37,15 @@ import rife.template.ValueRenderer;
*/ */
public class Capitalize implements 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.capitalize(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(StringUtils.capitalize(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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;
/**
* <p>Capitalizes words of a template value.</p>
*
* <p>Usage:</p>
*
* <pre>
* &lt;!--v render:rife.render.CapitalizeWords:valueId/--&gt;
* {{v render:rife.render.CapitalizeWords:valueId/}}
* </pre>
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @see <a href="https://github.com/rife2/rife2-template-renderers/wiki/rife.render.CapitalizeWords">rife.render.CapitalizeWords</a>
* @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)));
}
}

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import java.time.ZonedDateTime;
/** /**
* <p>Return the current date in ISO 8601 format.</p> * <p>Renders the current date in ISO 8601 format.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -38,10 +38,15 @@ import java.time.ZonedDateTime;
*/ */
public class DateIso implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import java.io.IOException;
import java.io.StringReader;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Properties;
/** /**
* <p>Return the current date and time in ISO 8601 format.</p> * <p>Renders the current date and time in ISO 8601 format.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -42,25 +39,28 @@ import java.util.Properties;
*/ */
public class DateTimeIso implements ValueRenderer { public class DateTimeIso implements ValueRenderer {
/** /**
* {@inheritDoc} * Renders the current date and time in ISO 8601 format.
*
* <p>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.</p>
*
* @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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
var defaultValue = template.getDefaultValue(valueId); var defaultValue = template.getDefaultValue(valueId);
if (defaultValue != null) { if (defaultValue != null) {
var properties = new Properties(); var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId));
try {
var tz = "tz"; var tz = "tz";
properties.load(new StringReader(defaultValue));
if (properties.containsKey(tz)) { if (properties.containsKey(tz)) {
return ZonedDateTime.now().format( return ZonedDateTime.now().format(
RenderUtils.ISO_8601_FORMATTER.withZone(ZoneId.of(properties.getProperty(tz)))); RenderUtils.ISO_8601_FORMATTER.withZone(ZoneId.of(properties.getProperty(tz))));
} }
} catch (IOException ignore) {
// do nothing
}
} }
return ZonedDateTime.now().format(RenderUtils.ISO_8601_FORMATTER); return template.getEncoder().encode(ZonedDateTime.now().format(RenderUtils.ISO_8601_FORMATTER));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import java.time.ZonedDateTime;
/** /**
* <p>Return the current date and time in RFC 2822 format.</p> * <p>Renders the current date and time in RFC 2822 format.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -38,10 +38,15 @@ import java.time.ZonedDateTime;
*/ */
public class DateTimeRfc2822 implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return StringUtils.encodeBase64(template.getValueOrAttribute(differentiator) var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId));
.getBytes(StandardCharsets.UTF_8)); return RenderUtils.encode(
StringUtils.encodeBase64(template.getValueOrAttribute(differentiator).getBytes(StandardCharsets.UTF_8)),
properties);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.formatCreditCard(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(RenderUtils.formatCreditCard(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
@ -45,6 +50,6 @@ public class Lowercase implements ValueRenderer {
if (value == null || value.isBlank()) { if (value == null || value.isBlank()) {
return value; return value;
} }
return value.toLowerCase(Localization.getLocale()); return template.getEncoder().encode(value.toLowerCase(Localization.getLocale()));
} }
} }

View file

@ -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;
/**
* <p>Masks characters of a template value.</p>
*
* <p>Usage:</p>
*
* <pre>
* &lt;!--v render:rife.render.Mask:valueId/--&gt;
* {{v render:rife.render.Mask:valueId/}}
* </pre>
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @see <a href="https://github.com/rife2/rife2-template-renderers/wiki/rife.render.Mask">rife.render.Mask</a>
* @since 1.0
*/
public class Mask implements ValueRenderer {
/**
* <p>Renders a template value with characters of the value masked using the specified mask.</p>
*
* <p>The mask is specified as a template default value with the following syntax:</p>
*
* <pre>
* mask=&lt;mask&gt;[,unmasked=&lt;unmasked&gt;][,fromStart=&lt;fromStart&gt;]
* </pre>
*
* <p>Where:</p>
*
* <ul>
* <li><var>mask</var> is the character to use for masking, defaulting to <code>*</code></li>
* <li><var>unmasked</var> is the number of characters at the beginning of the value that should be left unmasked,
* defaulting to <code>0</code></li>
* <li><var>fromStart</var> is a boolean indicating whether the <var>unmasked</var> value should be counted from
* the start of the value, defaulting to <code>false</code></li>
* </ul>
*
* @param template the template to render the value in
* @param valueId the ID of the value to render
* @param differentiator the differentiator of the value to render
* @return the rendered value
*/
@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 && !defaultValue.isBlank()) {
var properties = RenderUtils.parsePropertiesString(defaultValue);
mask = properties.getProperty("mask", mask);
try {
unmasked = Integer.parseInt(properties.getProperty("unmasked", "0"));
} catch (NumberFormatException ignored) {
// do nothing
}
fromStart = "true".equalsIgnoreCase(properties.getProperty("fromStart", "false"));
}
return template.getEncoder().encode(
RenderUtils.mask(template.getValueOrAttribute(differentiator), mask, unmasked, fromStart));
}
}

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,10 +36,15 @@ import rife.template.ValueRenderer;
*/ */
public class Normalize implements ValueRenderer { public class Normalize implements ValueRenderer {
/** /**
* {@inheritDoc} * Returns the template value normalized for inclusion in a URL path.
*
* @param template the template that contains the value
* @param valueId the id of the value
* @param differentiator the differentiator to use
* @return the normalized value
*/ */
@Override @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.normalize(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(RenderUtils.normalize(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,10 +20,6 @@ package rife.render;
import rife.template.Template; import rife.template.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
/** /**
* <p>Generates an SVG QR Code for a template value using <a href="https://goqr.me/">goQR.me</a>.</p> * <p>Generates an SVG QR Code for a template value using <a href="https://goqr.me/">goQR.me</a>.</p>
* *
@ -40,21 +36,17 @@ import java.util.Properties;
*/ */
public class QrCode implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
var size = "150x150"; var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId));
var defaultValue = template.getDefaultValue(valueId); var size = properties.getProperty("size", "150x150");
if (defaultValue != null) {
var properties = new Properties();
try {
properties.load(new StringReader(defaultValue));
size = properties.getProperty("size", size);
} catch (IOException ignore) {
// do nothing
}
}
return RenderUtils.qrCode(template.getValueOrAttribute(differentiator), size); return RenderUtils.qrCode(template.getValueOrAttribute(differentiator), size);
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 rife.tools.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.Normalizer; import java.text.Normalizer;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; 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. * Collection of utility-type methods commonly used by the renderers.
@ -39,6 +42,10 @@ import java.util.concurrent.TimeUnit;
* @since 1.0 * @since 1.0
*/ */
public final class RenderUtils { public final class RenderUtils {
/**
* The encoding property.
*/
public static final String ENCODING_PROPERTY = "encoding";
/** /**
* ISO 8601 date formatter. * 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()); DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss zzz").withLocale(Localization.getLocale());
private static final String DEFAULT_USER_AGENT = private static final String DEFAULT_USER_AGENT =
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"; "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() { private RenderUtils() {
// no-op // 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 src the source {@code String}
* @param max the maximum length of the resulting String * @param max the maximum length of the resulting {@code String}
* @param marker the String used as a replacement marker * @param marker the {@code String} used as a replacement marker
* @return the abbreviated String * @return the abbreviated {@code String}
*/ */
public static String abbreviate(String src, int max, String marker) { public static String abbreviate(String src, int max, String marker) {
if (src == null || src.isBlank() || marker == null) { 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. * 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}) * @return the .beat time. (eg.: {@code @248})
*/ */
public static String beatTime(ZonedDateTime zonedDateTime) { public static String beatTime(ZonedDateTime zonedDateTime) {
var zdt = zonedDateTime.withZoneSameInstant(ZoneId.of("UTC+01:00")); 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) + var beats = (int) ((zdt.getSecond() + (zdt.getMinute() * 60) +
(zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4); (zdt.getHour() * 3600)) / 86.4);
return String.format("@%03d", beats); 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 * @param src the source {@code String}
* @return the capitalized String * @return the capitalized {@code String}
*/ */
public static String capitalize(String src) { public static String capitalizeWords(String src) {
if (src == null || src.isBlank()) { if (src == null || src.isBlank()) {
return src; 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. * <p>Encodes the source {@code String} to the specified encoding.</p>
* *
* @param src the source String * <p>The supported encodings are:</p>
* @return the encoded String *
* <ul>
* <li>{@code html}</li>
* <li>{@code js}</li>
* <li>{@code json}</li>
* <li>{@code unicode}</li>
* <li>{@code url}</li>
* <li>{@code xml}</li>
* </ul>
*
* @param src the source {@code String} to encode
* @param properties the properties containing the {@link #ENCODING_PROPERTY encoding property}.
* @return the encoded {@code String}
*/
public static String encode(String src, Properties properties) {
if (src == null || src.isBlank() || properties.isEmpty()) {
return src;
}
var encoding = properties.getProperty(ENCODING_PROPERTY, "");
switch (encoding) {
case "html" -> {
return StringUtils.encodeHtml(src);
}
case "js" -> {
return encodeJs(src);
}
case "json" -> {
return StringUtils.encodeJson(src);
}
case "unicode" -> {
return StringUtils.encodeUnicode(src);
}
case "url" -> {
return StringUtils.encodeUrl(src);
}
case "xml" -> {
return StringUtils.encodeXml(src);
}
default -> {
return src;
}
}
}
/**
* Encodes a {@code String} to JavaScript/ECMAScript.
*
* @param src the source {@code String}
* @return the encoded {@code String}
*/ */
public static String encodeJs(String src) { public static String encodeJs(String src) {
if (src == null || src.isBlank()) { if (src == null || src.isBlank()) {
@ -151,6 +227,11 @@ public final class RenderUtils {
case '"' -> sb.append("\\\""); case '"' -> sb.append("\\\"");
case '\\' -> sb.append("\\\\"); case '\\' -> sb.append("\\\\");
case '/' -> sb.append("\\/"); case '/' -> sb.append("\\/");
case '\b' -> sb.append("\\b");
case '\n' -> sb.append(("\\n"));
case '\t' -> sb.append("\\t");
case '\f' -> sb.append("\\f");
case '\r' -> sb.append("\\r");
default -> sb.append(c); default -> sb.append(c);
} }
} }
@ -160,29 +241,50 @@ public final class RenderUtils {
/** /**
* Fetches the content (body) of a URL. * Fetches the content (body) of a URL.
* *
* @param url the URL sSng. * @param url the URL {@code String}
* @param defaultContent the default content to return if none fetched. * @param defaultContent the default content to return if none fetched
* @return the url content, or empty. * @return the url content, or empty
*/ */
public static String fetchUrl(String url, String defaultContent) { public static String fetchUrl(String url, String defaultContent) {
try { try {
var connection = (HttpURLConnection) new URL(url).openConnection(); var fetchUrl = new URL(url);
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) fetchUrl.openConnection();
connection.setRequestProperty("User-Agent", DEFAULT_USER_AGENT); connection.setRequestProperty("User-Agent", DEFAULT_USER_AGENT);
var code = connection.getResponseCode(); var code = connection.getResponseCode();
if (code >= 200 && code <= 399) { if (code >= 200 && code <= 399) {
try (var inputStream = connection.getInputStream()) { try (var inputStream = connection.getInputStream()) {
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
} }
} else {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.warning("A " + code + " status code was returned by " + fetchUrl.getHost());
} }
} catch (IOException ignore) { }
} catch (IOException ioe) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "An IO error occurred while connecting to " + fetchUrl.getHost(), ioe);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
} catch (MalformedURLException ignored) {
// do nothing // do nothing
} }
return defaultContent; return defaultContent;
} }
/** /**
* Returns the last 4 digits a credit card number. The number must satisfy the Luhn algorithm. * <p>Returns the last 4 digits a credit card number.</p>
* Non-digits are stripped from the number. *
* <ul>
* <li>The number must satisfy the Luhn algorithm</li>
* <li>Non-digits are stripped from the number</li>
* </ul>
* *
* @param src the credit card number * @param src the credit card number
* @return the last 4 digits of the credit card number or empty * @return the last 4 digits of the credit card number or empty
@ -192,45 +294,20 @@ public final class RenderUtils {
return src; return src;
} }
try {
var cc = src.replaceAll("[^0-9]", ""); var cc = src.replaceAll("[^0-9]", "");
var len = cc.length(); if (validateCreditCard(cc)) {
if (len >= 4) { return cc.substring(cc.length() - 4);
// Luhn algorithm } else {
var sum = 0;
boolean isSecond = 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 (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 ""; return "";
} }
}
/** /**
* Converts a text String to HTML decimal entities. * Converts a text {@code String} to HTML decimal entities.
* *
* @param src the String to convert * @param src the {@code String} to convert
* @return the converted String * @return the converted {@code String}
*/ */
@SuppressWarnings("PMD.AvoidReassigningLoopVariables") @SuppressWarnings("PMD.AvoidReassigningLoopVariables")
public static String htmlEntities(String src) { public static String htmlEntities(String src) {
@ -257,11 +334,11 @@ public final class RenderUtils {
/** /**
* Masks characters in a String. * Masks characters in a String.
* *
* @param src the source String. * @param src the source {@code String}
* @param mask the String to mask characters with * @param mask the {@code String} to mask characters with
* @param unmasked the number of characters to leave unmasked * @param unmasked the number of characters to leave unmasked
* @param fromStart to unmask characters from the start of the String * @param fromStart to unmask characters from the start of the {@code String}
* @return the masked String * @return the masked {@code String}
*/ */
public static String mask(String src, String mask, int unmasked, boolean fromStart) { public static String mask(String src, String mask, int unmasked, boolean fromStart) {
if (src == null || src.isEmpty()) { if (src == null || src.isEmpty()) {
@ -285,44 +362,61 @@ public final class RenderUtils {
} }
/** /**
* Normalizes a String for inclusion in a URL path. * Normalizes a {@code String} for inclusion in a URL path.
* *
* @param src The source String * @param src the source {@code String}
* @return The normalized String * @return the normalized {@code String}
*/ */
public static String normalize(String src) { public static String normalize(String src) {
if (src == null || src.isBlank()) { if (src == null || src.isBlank()) {
return src; return src;
} }
var normalized = Normalizer.normalize(src.trim(), Normalizer.Form.NFD); var normalized = Normalizer.normalize(src.trim(), Normalizer.Form.NFD).toCharArray();
var sb = new StringBuilder(normalized.length());
boolean space = false; var sb = new StringBuilder(normalized.length);
for (var c : normalized.toCharArray()) { for (var i = 0; i < normalized.length; i++) {
if (c <= '\u007F') { // ascii only var c = normalized[i];
if (!space && c == ' ') { if (c <= '\u007F') { // ASCII only
space = true; if (" &()-_=[{]}\\|;:,<.>/".indexOf(c) != -1) { // common separators
if (!sb.isEmpty() && i != normalized.length - 1 && sb.charAt(sb.length() - 1) != '-') {
sb.append('-'); sb.append('-');
} else {
space = false;
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
sb.append(c);
} else if (c >= 'A' && c <= 'Z') {
sb.append((char) (c + 32)); // lowercase
} }
} else if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z') { // letters & digits
sb.append(c);
} else if (c >= 'A' && c <= 'Z') { // uppercase letters
sb.append((char) (c + 32)); // make lowercase
} }
} }
} }
return sb.toString(); return sb.toString();
} }
/**
* Returns a new {@code Properties} containing the properties specified in the given {@code String}.
*
* @param src the {@code} String containing the properties
* @return the new {@code Properties}
*/
public static Properties parsePropertiesString(String src) {
var properties = new Properties();
if (src != null && !src.isBlank()) {
try {
properties.load(new StringReader(src));
} catch (IOException ignored) {
// ignore
}
}
return properties;
}
/** /**
* Returns the plural form of a word, if count &gt; 1. * Returns the plural form of a word, if count &gt; 1.
* *
* @param count the count * @param count the count
* @param word the singular word * @param word the singular word
* @param plural the plural word * @param plural the plural word
* @return the singular or plural String * @return the singular or plural {@code String}
*/ */
public static String plural(final long count, final String word, final String plural) { public static String plural(final long count, final String word, final String plural) {
if (count > 1) { if (count > 1) {
@ -333,9 +427,9 @@ public final class RenderUtils {
} }
/** /**
* Generates an SVG QR Code from the given String using <a href="https://goqr.me/">goQR.me</a>. * Generates an SVG QR Code from the given {@code String} using <a href="https://goqr.me/">goQR.me</a>.
* *
* @param src the data String * @param src the data {@code String}
* @param size the QR Code size. (e.g. {@code 150x150}) * @param size the QR Code size. (e.g. {@code 150x150})
* @return the QR code * @return the QR code
*/ */
@ -350,10 +444,10 @@ public final class RenderUtils {
} }
/** /**
* Translates a String to/from ROT13. * Translates a {@code String} to/from ROT13.
* *
* @param src the source String * @param src the source {@code String}
* @return the translated String * @return the translated {@code String}
*/ */
public static String rot13(String src) { public static String rot13(String src) {
if (src == null || src.isBlank()) { if (src == null || src.isBlank()) {
@ -366,7 +460,7 @@ public final class RenderUtils {
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
var inChar = src.charAt(i); var inChar = src.charAt(i);
if ((inChar >= 'A') && (inChar <= 'Z')) { if (inChar >= 'A' && inChar <= 'Z') {
inChar += (char) 13; inChar += (char) 13;
if (inChar > 'Z') { if (inChar > 'Z') {
@ -374,7 +468,7 @@ public final class RenderUtils {
} }
} }
if ((inChar >= 'a') && (inChar <= 'z')) { if (inChar >= 'a' && inChar <= 'z') {
inChar += (char) 13; inChar += (char) 13;
if (inChar > 'z') { if (inChar > 'z') {
@ -391,7 +485,7 @@ public final class RenderUtils {
/** /**
* <p>Shortens a URL using <a href="https://is.gd/">is.gid</a>.</p> * <p>Shortens a URL using <a href="https://is.gd/">is.gid</a>.</p>
* *
* <p>The URL String must be a valid http or https URL.</p> * <p>The URL {@code String} must be a valid http or https URL.</p>
* *
* <p>Based on <a href="https://github.com/ethauvin/isgd-shorten">isgd-shorten</a></p> * <p>Based on <a href="https://github.com/ethauvin/isgd-shorten">isgd-shorten</a></p>
* *
@ -409,8 +503,8 @@ public final class RenderUtils {
/** /**
* Swaps the case of a String. * Swaps the case of a String.
* *
* @param src the String to swap the case of * @param src the {@code String} to swap the case of
* @return the modified String or null * @return the modified {@code String} or null
*/ */
@SuppressWarnings("PMD.AvoidReassigningLoopVariables") @SuppressWarnings("PMD.AvoidReassigningLoopVariables")
public static String swapCase(String src) { public static String swapCase(String src) {
@ -438,19 +532,6 @@ public final class RenderUtils {
return new String(buff, 0, offset); 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);
}
/** /**
* <p>Returns the formatted server uptime.</p> * <p>Returns the formatted server uptime.</p>
* *
@ -473,7 +554,7 @@ public final class RenderUtils {
* *
* @param uptime the uptime in milliseconds * @param uptime the uptime in milliseconds
* @param properties the format properties * @param properties the format properties
* @return the formatted uptime. * @return the formatted uptime
*/ */
@SuppressWarnings("UnnecessaryUnicodeEscape") @SuppressWarnings("UnnecessaryUnicodeEscape")
public static String uptime(long uptime, Properties properties) { public static String uptime(long uptime, Properties properties) {
@ -520,4 +601,43 @@ public final class RenderUtils {
return sb.toString(); 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;
}
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.rot13(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(RenderUtils.rot13(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { public class ShortenUrl implements ValueRenderer {
/** /**
* {@inheritDoc} * Returns the template value shortened using <a href="https://is.gd/">is.gid</a>.
*
* @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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.shortenUrl(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(RenderUtils.shortenUrl(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import rife.template.ValueRenderer;
/** /**
* <p>Swap case of a template value.</p> * <p>Swaps case of a template value.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -35,12 +35,16 @@ import rife.template.ValueRenderer;
* @since 1.0 * @since 1.0
*/ */
public class SwapCase implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.swapCase(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(RenderUtils.swapCase(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import java.time.ZonedDateTime;
/** /**
* <p>Return the current time in ISO 8601 format.</p> * <p>Renders the current time in ISO 8601 format.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -38,10 +38,15 @@ import java.time.ZonedDateTime;
*/ */
public class TimeIso implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
@ -44,6 +49,6 @@ public class Trim implements ValueRenderer {
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty()) {
return value; return value;
} }
return value.trim(); return template.getEncoder().encode(value.trim());
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import rife.tools.StringUtils;
/** /**
* <p>Un-capitalizes a template value.</p> * <p>Un-capitalizes a template value.</p>
@ -36,10 +37,15 @@ import rife.template.ValueRenderer;
*/ */
public class Uncapitalize implements 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
return RenderUtils.uncapitalize(template.getValueOrAttribute(differentiator)); return template.getEncoder().encode(StringUtils.uncapitalize(template.getValueOrAttribute(differentiator)));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import rife.tools.Localization;
/** /**
* <p>Convert a template value to uppercase.</p> * <p>Converts a template value to uppercase.</p>
* *
* <p>Usage:</p> * <p>Usage:</p>
* *
@ -37,7 +37,12 @@ import rife.tools.Localization;
*/ */
public class Uppercase implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
@ -45,6 +50,6 @@ public class Uppercase implements ValueRenderer {
if (value == null || value.isBlank()) { if (value == null || value.isBlank()) {
return value; return value;
} }
return value.toUpperCase(Localization.getLocale()); return template.getEncoder().encode(value.toUpperCase(Localization.getLocale()));
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Template;
import rife.template.ValueRenderer; import rife.template.ValueRenderer;
import java.io.IOException;
import java.io.StringReader;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.util.Properties;
/** /**
* Renders the server uptime. * Renders the server uptime.
@ -41,24 +38,23 @@ import java.util.Properties;
*/ */
public class Uptime implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { public String render(Template template, String valueId, String differentiator) {
var properties = new Properties(); var properties = RenderUtils.parsePropertiesString(template.getDefaultValue(valueId));
var defaultValue = template.getDefaultValue(valueId); String uptime;
if (defaultValue != null) { if (template.hasAttribute(Uptime.class.getName())) {
try { uptime = RenderUtils.uptime((long) template.getAttribute(Uptime.class.getName()), properties);
properties.load(new StringReader(defaultValue)); } else {
} catch (IOException ignore) { uptime = RenderUtils.uptime(ManagementFactory.getRuntimeMXBean().getUptime(), properties);
// ignore
}
} }
if (template.hasAttribute(Uptime.class.getName())) { return template.getEncoder().encode(uptime);
return RenderUtils.uptime((long) template.getAttribute(Uptime.class.getName()), properties);
} else {
return RenderUtils.uptime(ManagementFactory.getRuntimeMXBean().getUptime(), properties);
}
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * @since 1.0
*/ */
public class Year implements ValueRenderer { 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 @Override
public String render(Template template, String valueId, String differentiator) { 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));
} }
} }

View file

@ -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 <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @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");
}
}
}

View file

@ -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 <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisableOnCiCondition.class)
public @interface DisabledOnCi {
}

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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."); var bean = new ValueBean("this IS a TEST.");
t.setBean(bean); t.setBean(bean);
assertThat(t.getContent()).isEqualTo(bean.getValue() + ": this is a test."); assertThat(t.getContent()).isEqualTo(bean.getValue() + ": this is a test.");
bean = new ValueBean("");
t.setBean(bean);
assertThat(t.getContent()).isEqualTo(bean.getValue() + ": ");
} }
@Test @Test
@ -54,6 +57,8 @@ class TestCase {
var t = TemplateFactory.TXT.get("trim"); var t = TemplateFactory.TXT.get("trim");
t.setAttribute(FOO, "\t" + SAMPLE_TEXT + " \n"); t.setAttribute(FOO, "\t" + SAMPLE_TEXT + " \n");
assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT);
t.setAttribute(FOO, "");
assertThat(t.getContent()).isEmpty();
} }
@Test @Test
@ -68,5 +73,7 @@ class TestCase {
var t = TemplateFactory.TXT.get("uppercase"); var t = TemplateFactory.TXT.get("uppercase");
t.setAttribute("bar", SAMPLE_TEXT); t.setAttribute("bar", SAMPLE_TEXT);
assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT.toUpperCase(Localization.getLocale())); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT.toUpperCase(Localization.getLocale()));
t.setAttribute("bar", "");
assertThat(t.getContent()).isEmpty();
} }
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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"); var t = TemplateFactory.TXT.get("encodeBase64");
t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT); t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT);
assertThat(t.getContent()).isEqualTo(t.getValue(TestCase.FOO) + ": VGhpcyBpcyBhIHRlc3Qu"); 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 @Test
@ -50,6 +54,15 @@ class TestEncode {
var t = TemplateFactory.TXT.get("encodeJs"); var t = TemplateFactory.TXT.get("encodeJs");
t.setAttribute(TestCase.FOO, "'\"\\/"); t.setAttribute(TestCase.FOO, "'\"\\/");
assertThat(t.getContent()).isEqualTo("\\'\\\"\\\\\\/"); 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 @Test
@ -57,6 +70,10 @@ class TestEncode {
var t = TemplateFactory.JSON.get("encodeJson"); var t = TemplateFactory.JSON.get("encodeJson");
t.setAttribute(TestCase.FOO, "This is a \"•test\""); t.setAttribute(TestCase.FOO, "This is a \"•test\"");
assertThat(t.getContent()).isEqualTo("{\n \"foo\": \"This is a \\\"\\u2022test\\\"\"\n}"); assertThat(t.getContent()).isEqualTo("{\n \"foo\": \"This is a \\\"\\u2022test\\\"\"\n}");
t = TemplateFactory.HTML.get("encodeJson");
t.setAttribute(TestCase.FOO, "\"<test>\"");
assertThat(t.getContent()).as("with html").isEqualTo("\\&quot;&lt;test&gt;\\&quot;");
} }
@Test @Test
@ -80,6 +97,11 @@ class TestEncode {
t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT);
assertThat(t.getContent()).isEqualTo( assertThat(t.getContent()).isEqualTo(
"\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E"); "\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E");
t = TemplateFactory.HTML.get("encodeUnicode");
t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"');
assertThat(t.getContent()).as("with js")
.contains("'\\\\u0022\\\\u0054\\\\u0068\\\\u0069\\\\u0073\\\\u0020\\\\u0069\\\\u0073\\\\u0020\\\\u0061\\\\u0020\\\\u0074\\\\u0065\\\\u0073\\\\u0074\\\\u002E\\\\u0022'");
} }
@Test @Test
@ -87,6 +109,11 @@ class TestEncode {
var t = TemplateFactory.HTML.get("encodeUrl"); var t = TemplateFactory.HTML.get("encodeUrl");
t.setAttribute(TestCase.FOO, "a test &"); t.setAttribute(TestCase.FOO, "a test &");
assertThat(t.getContent()).isEqualTo("<a href=\"https://example.com/a%20test%20%26\">a test &amp;</a>"); assertThat(t.getContent()).isEqualTo("<a href=\"https://example.com/a%20test%20%26\">a test &amp;</a>");
t = TemplateFactory.HTML.get("encodeUrlwithUnicode");
t.setAttribute(TestCase.FOO, "a=test");
assertThat(t.getContent()).as("with unicode")
.contains("https://foo.com/\\u0061\\u0025\\u0033\\u0044\\u0074\\u0065\\u0073\\u0074");
} }
@Test @Test

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,7 +27,7 @@ class TestFormat {
void testAbbreviate() { void testAbbreviate() {
var t = TemplateFactory.HTML.get("abbreviate"); var t = TemplateFactory.HTML.get("abbreviate");
t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT);
assertThat(t.getContent()).as("activate.html").endsWith("").hasSize(12); assertThat(t.getContent()).as("activate.html").endsWith("&hellip;").hasSize(19);
t = TemplateFactory.TXT.get("abbreviate"); t = TemplateFactory.TXT.get("abbreviate");
t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT);
@ -44,7 +44,9 @@ class TestFormat {
t.setAttribute(TestCase.FOO, "374380141731053"); t.setAttribute(TestCase.FOO, "374380141731053");
assertThat(t.getContent()).as("UK AMEX").isEqualTo("1053"); assertThat(t.getContent()).as("UK AMEX").isEqualTo("1053");
t.setAttribute(TestCase.FOO, "000000000000001"); t.setAttribute(TestCase.FOO, "000000000000001");
assertThat(t.getContent()).isEmpty(); assertThat(t.getContent()).as("000000000000001").isEmpty();
t.setAttribute(TestCase.FOO, "");
assertThat(t.getContent()).as("").isEmpty();
} }
@Test @Test
@ -52,7 +54,8 @@ class TestFormat {
var t = TemplateFactory.HTML.get("mask"); var t = TemplateFactory.HTML.get("mask");
var foo = "374380141731053"; var foo = "374380141731053";
t.setAttribute(TestCase.FOO, foo); t.setAttribute(TestCase.FOO, foo);
assertThat(t.getContent()).as("mask.html").isEqualTo("3743•••••••••••"); assertThat(t.getContent()).as("mask.html")
.isEqualTo("3743&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;");
t = TemplateFactory.TXT.get("mask"); t = TemplateFactory.TXT.get("mask");
t.setAttribute(TestCase.FOO, foo); t.setAttribute(TestCase.FOO, foo);
@ -69,6 +72,7 @@ class TestFormat {
} }
@Test @Test
@DisabledOnCi
void testQrCode() { void testQrCode() {
var t = TemplateFactory.SVG.get("qrCode"); var t = TemplateFactory.SVG.get("qrCode");
var foo = "https://example.com/"; var foo = "https://example.com/";
@ -77,6 +81,7 @@ class TestFormat {
} }
@Test @Test
@DisabledOnCi
void testShortenUrl() { void testShortenUrl() {
var t = TemplateFactory.HTML.get("shortenUrl"); var t = TemplateFactory.HTML.get("shortenUrl");
var url = "https://example.com/"; var url = "https://example.com/";
@ -95,7 +100,7 @@ class TestFormat {
t = TemplateFactory.HTML.get("uptime"); t = TemplateFactory.HTML.get("uptime");
t.setAttribute(Uptime.class.getName(), 547800300076L); t.setAttribute(Uptime.class.getName(), 547800300076L);
assertThat(t.getContent()).as("uptime.html") assertThat(t.getContent()).as("uptime.html")
.isEqualTo("17 années, 4 mois, 2 semaines, 1 jour, 6 heures, 45 minutes"); .isEqualTo("17 ann&eacute;es, 4 mois, 2 semaines, 1 jour, 6 heures, 45 minutes");
t.setAttribute(Uptime.class.getName(), 120000L); t.setAttribute(Uptime.class.getName(), 120000L);
assertThat(t.getContent()).as("uptime.html: 2 min").isEqualTo("2 minutes"); assertThat(t.getContent()).as("uptime.html: 2 min").isEqualTo("2 minutes");

View file

@ -0,0 +1,150 @@
/*
* 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.assertj.core.api.AutoCloseableSoftAssertions;
import org.junit.jupiter.api.Test;
import java.util.Properties;
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);
assertThat(RenderUtils.abbreviate("", 10, "")).as("").isEmpty();
}
@Test
void testCapitalizeWords() {
assertThat(RenderUtils.capitalizeWords("hello world")).isEqualTo("Hello World");
assertThat(RenderUtils.capitalizeWords("java programming")).isEqualTo("Java Programming");
assertThat(RenderUtils.capitalizeWords("TEST")).isEqualTo("Test");
assertThat(RenderUtils.capitalizeWords("multiple spaces")).isEqualTo("Multiple Spaces");
assertThat(RenderUtils.capitalizeWords("white\t\fspaces")).isEqualTo("White\t\fSpaces");
assertThat(RenderUtils.capitalizeWords("")).isEmpty();
assertThat(RenderUtils.capitalizeWords(null)).isNull();
}
@Test
void testEncode() {
var p = new Properties();
p.put(RenderUtils.ENCODING_PROPERTY, "blah");
assertThat(RenderUtils.encode(TestCase.SAMPLE_TEXT, p)).as("invalid encoding").isEqualTo(TestCase.SAMPLE_TEXT);
p.put(RenderUtils.ENCODING_PROPERTY, "json");
assertThat(RenderUtils.encode("This is a \"•test\"", p)).as("json").isEqualTo("This is a \\\"\\u2022test\\\"");
p.put(RenderUtils.ENCODING_PROPERTY, "html");
assertThat(RenderUtils.encode("<a test &>", p)).as("html").isEqualTo("&lt;a test &amp;&gt;");
p.put(RenderUtils.ENCODING_PROPERTY, "js");
assertThat(RenderUtils.encode("\"test'", p)).as("js").isEqualTo("\\\"test\\'");
p.put(RenderUtils.ENCODING_PROPERTY, "unicode");
assertThat(RenderUtils.encode("test", p)).as("unicode").isEqualTo("\\u0074\\u0065\\u0073\\u0074");
p.put(RenderUtils.ENCODING_PROPERTY, "url");
assertThat(RenderUtils.encode("a = test", p)).as("url").isEqualTo("a%20%3D%20test");
p.put(RenderUtils.ENCODING_PROPERTY, "xml");
assertThat(RenderUtils.encode("Joe's Café & Bar", p)).as("xml").isEqualTo("Joe&apos;s Café &amp; Bar");
}
@Test
void testEncodeJs() {
assertThat(RenderUtils.encodeJs("")).isEmpty();
}
@Test
void testFetchUrl() {
var s = "default";
assertThat(RenderUtils.fetchUrl("blah", s)).isEqualTo(s);
assertThat(RenderUtils.fetchUrl("https://www.google.com/404", s)).isEqualTo(s);
assertThat(RenderUtils.fetchUrl("https://www.notreallythere.com/", s)).isEqualTo(s);
}
@Test
void testHtmlEntities() {
assertThat(RenderUtils.htmlEntities("")).isEmpty();
assertThat(RenderUtils.htmlEntities(SAMPLE_GERMAN))
.isEqualTo("&#77;&#246;&#99;&#104;&#116;&#101;&#110;&#32;&#83;&#105;&#101;&#32;&#101;&#105;&#110;&#32;&#112;&#97;&#97;&#114;&#32;&#196;&#112;&#102;&#101;&#108;&#63;");
}
@Test
void testMask() {
var foo = "4342256562440179";
assertThat(RenderUtils.mask("", " ", 2, false)).isEmpty();
assertThat(RenderUtils.mask(foo, "?", 4, false)).as("mask=?")
.isEqualTo("????????????0179");
assertThat(RenderUtils.mask(foo, "-", 22, true)).as("unmasked=22")
.isEqualTo("----------------");
assertThat(RenderUtils.mask(foo, "&bull;", -1, false)).as("mask=&bull;")
.isEqualTo("&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;");
}
@Test
void testNormalize() {
assertThat(RenderUtils.normalize("")).as("empty").isEmpty();
assertThat(RenderUtils.normalize(" &()-_=[{]}\\|;:,<.>/")).as("blank").isEmpty();
assertThat(RenderUtils.normalize(SAMPLE_GERMAN)).as("greman").isEqualTo("mochten-sie-ein-paar-apfel");
assertThat(RenderUtils.normalize("foo bar, <foo-bar>,foo:bar,foo;(bar), {foo} & bar=foo.bar[foo|bar]"))
.as("foo-bar")
.isEqualTo("foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar");
assertThat(RenderUtils.normalize("News for January 6, 2023 (Paris)")).as("docs example")
.isEqualTo("news-for-january-6-2023-paris");
}
@Test
void testQrCode() {
assertThat(RenderUtils.qrCode("", "12")).isEmpty();
}
@Test
void testRot13() {
var encoded = "Zöpugra Fvr rva cnne Äcsry?";
assertThat(RenderUtils.rot13("")).isEmpty();
assertThat(RenderUtils.rot13(SAMPLE_GERMAN)).as("encode").isEqualTo(encoded);
assertThat(RenderUtils.rot13(encoded)).as("decode").isEqualTo(SAMPLE_GERMAN);
}
@Test
void testSwapCase() {
assertThat(RenderUtils.swapCase("")).isEmpty();
assertThat(RenderUtils.swapCase(SAMPLE_GERMAN)).isEqualTo("mÖCHTEN sIE EIN PAAR äPFEL?");
}
@Test
void testValidateCreditCard() {
try (var softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(RenderUtils.validateCreditCard("4505 4672 3366 6430")).as("visa").isTrue();
softly.assertThat(RenderUtils.validateCreditCard("5189-5923-3915-0425")).as("mastercard").isTrue();
softly.assertThat(RenderUtils.validateCreditCard("3433634926643302")).as("amex").isTrue();
softly.assertThat(RenderUtils.validateCreditCard("6011 1076-8252 0629")).as("discover").isTrue();
softly.assertThat(RenderUtils.validateCreditCard("0123456789012345")).as("invalid").isFalse();
}
}
}

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -0,0 +1 @@
<a href="https://foo.com/{{v render:rife.render.EncodeBase64:foo}}encoding=url{{/v}}"><!--v foo/--></a>

View file

@ -0,0 +1 @@
<!--v render:rife.render.EncodeJs:foo-->encoding=unicode<!--/v-->

View file

@ -0,0 +1 @@
<!--v render:rife.render.EncodeJson:foo-->encoding=html<!--/v-->

View file

@ -0,0 +1 @@
<script>alert('{{v render:rife.render.EncodeUnicode:foo}}encoding=js{{/v}}');</script>

View file

@ -0,0 +1 @@
<script>window.open("https://foo.com/{{v render:rife.render.EncodeUrl:foo}}encoding=unicode{{/v}}")</script>

Some files were not shown because too many files have changed in this diff Show more