Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0116afd
Adding basic JaCoCo
jenspapenhagen Nov 12, 2025
9efaed6
Adding gitHub Action for generating a coverage badge
jenspapenhagen Nov 12, 2025
17023d5
Adding coverage badge to README.md
jenspapenhagen Nov 12, 2025
342db94
Adding customJacocoReportDir for cleaner build folder
jenspapenhagen Nov 12, 2025
b0a8aa1
Adding Coverage Verification for line and for branch coverage
jenspapenhagen Nov 12, 2025
343673c
Adding junit example for testing the UnsupportedOperationException in…
jenspapenhagen Nov 12, 2025
5c864d0
Adding junit test for the UnsupportedOperationException in the privat…
jenspapenhagen Nov 12, 2025
a77a647
use jackson 3 methode for getting the string out of a jsonNode
jenspapenhagen Nov 12, 2025
c240d1d
Adding test for StringValidator
jenspapenhagen Nov 12, 2025
c334c15
Adding change pattern order StringValidator, first octal than numeric…
jenspapenhagen Nov 12, 2025
603cbc8
Adding test for PrimitiveDecoder
jenspapenhagen Nov 12, 2025
9b42f21
Use the same octal number pattern regex in PrimitiveDecoder
jenspapenhagen Nov 12, 2025
2e27db1
Adding Constructor for test coverage
jenspapenhagen Nov 12, 2025
8e74625
refactor ValueDecoder 1/n
jenspapenhagen Nov 12, 2025
9802357
refactor ValueDecoder 2/n
jenspapenhagen Nov 12, 2025
d9c6789
adding jacoco to the ci/cd
jenspapenhagen Nov 12, 2025
465d100
fix merge conflict
jenspapenhagen Nov 12, 2025
f2b6abf
Update Jacoco Badge
github-actions[bot] Nov 12, 2025
9cea432
Merge pull request #13 from jenspapenhagen/jacoco
jenspapenhagen Nov 12, 2025
664c718
Update build.yml
jenspapenhagen Nov 13, 2025
eaa4d8b
Update release.yml
jenspapenhagen Nov 13, 2025
4f8fc2b
mini change for trigger release
jenspapenhagen Nov 13, 2025
7c83c02
lint release.yml
jenspapenhagen Nov 13, 2025
528c567
undo change
jenspapenhagen Nov 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 24 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ on:
jobs:
build:
runs-on: ubuntu-latest


permissions:
contents: write
pull-requests: write

steps:
- name: Checkout code
uses: actions/checkout@v5
Expand All @@ -40,7 +44,25 @@ jobs:
name: test-results
path: build/reports/tests/test/
retention-days: 7


- name: Add coverage to PR
id: jacoco
uses: madrapps/[email protected]
with:
paths: ${{ github.workspace }}/build/customJacocoReportDir/test/jacocoTestReport.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: 85
min-coverage-changed-files: 75
title: Code Coverage
update-comment: true

- name: Fail PR if overall coverage is less than 85%
if: ${{ steps.jacoco.outputs.coverage-overall < 85.0 }}
uses: actions/github-script@v6
with:
script: |
core.setFailed('Overall coverage is less than 85%!')

- name: Upload build artifacts
uses: actions/upload-artifact@v5
with:
Expand Down
35 changes: 24 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
with:
ref: main
fetch-depth: 0

- name: Extract version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
Expand All @@ -29,20 +29,20 @@ jobs:
echo "version=$VERSION" > gradle.properties
echo "gradle.properties criado:"
cat gradle.properties

- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
cache: 'gradle'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew build

- name: Validate Maven Central credentials
run: |
if [ -z "${{ secrets.MAVEN_CENTRAL_TOKEN }}" ]; then
Expand All @@ -52,23 +52,23 @@ jobs:
echo "✅ Maven Central token is set (length: ${#MAVEN_CENTRAL_TOKEN})"
env:
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}

- name: Check curl version
run: curl --version

- name: Publish to Maven Central
run: ./gradlew publishToMavenCentral
env:
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

- name: Update README version
run: |
VERSION=${{ steps.get_version.outputs.VERSION }}
sed -i "s/jtoon:[0-9]\+\.[0-9]\+\.[0-9]\+/jtoon:$VERSION/g" README.md
sed -i "s/<version>[0-9]\+\.[0-9]\+\.[0-9]\+<\/version>/<version>$VERSION<\/version>/g" README.md

- name: Commit README changes
run: |
git config user.name "github-actions[bot]"
Expand All @@ -78,7 +78,7 @@ jobs:
git push origin main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
Expand All @@ -88,7 +88,20 @@ jobs:
prerelease: ${{ contains(steps.get_version.outputs.VERSION, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


- name: Generate JaCoCo Badge
uses: cicirello/[email protected]
with:
generate-branches-badge: true
jacoco-csv-file: build/customJacocoReportDir/test/jacocoTestReport.csv

- name: Update Jacoco Badge
uses: test-room-7/[email protected]
with:
file-path: .github/badges/jacoco.svg
commit-msg: Update Jacoco Badge
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Upload build artifacts
uses: actions/upload-artifact@v5
if: always()
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![Build](https://github.com/felipestanzani/jtoon/actions/workflows/build.yml/badge.svg)](https://github.com/felipestanzani/jtoon/actions/workflows/build.yml)
[![Release](https://github.com/felipestanzani/jtoon/actions/workflows/release.yml/badge.svg)](https://github.com/felipestanzani/jtoon/actions/workflows/release.yml)
[![Maven Central](https://img.shields.io/maven-central/v/com.felipestanzani/jtoon.svg)](https://central.sonatype.com/artifact/com.felipestanzani/jtoon)
![Coverage](.github/badges/jacoco.svg)

**Token-Oriented Object Notation** is a compact, human-readable format designed for passing structured data to Large Language Models with significantly reduced token usage.

Expand Down
39 changes: 38 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'java'
id 'maven-publish'
id 'signing'
id 'jacoco'
}

group = 'com.felipestanzani'
Expand All @@ -28,6 +29,11 @@ repositories {
mavenCentral()
}

jacoco {
toolVersion = "0.8.14"
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
}

dependencies {
implementation 'tools.jackson.core:jackson-databind:3.0.2'
implementation 'tools.jackson.module:jackson-module-afterburner:3.0.2'
Expand All @@ -38,4 +44,35 @@ dependencies {

test {
useJUnitPlatform()
}
finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestReport {
dependsOn test
reports {
csv.required = true
xml.required = true
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}

jacocoTestCoverageVerification {
violationRules {
rule {
enabled = true
element = "BUNDLE"
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = "0.85".toBigDecimal()
}

limit {
counter = "BRANCH"
value = "COVEREDRATIO"
minimum = "0.80".toBigDecimal()
}
}
}
}
check.dependsOn jacocoTestReport
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ static Object parse(String value) {

// Check for leading zeros (treat as string, except for "0", "-0", "0.0", etc.)
String trimmed = value.trim();
if (trimmed.length() > 1 && trimmed.matches("^-?0+[\\d].*")
if (trimmed.length() > 1
&& trimmed.matches("^-?0+[0-7].*") //octal number
&& !trimmed.matches("^-?0+(\\.0+)?([eE][+-]?\\d+)?$")) {
return value;
}
Expand Down
42 changes: 22 additions & 20 deletions src/main/java/com/felipestanzani/jtoon/decoder/ValueDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ private List<Object> parseTabularArray(String header, int depth, String arrayDel
* Returns true if parsing should continue, false if array should terminate.
*/
private boolean processTabularArrayLine(int depth, List<String> keys, String arrayDelimiter,
List<Object> result) {
List<Object> result) {
String line = lines[currentLine];

if (isBlankLine(line)) {
Expand Down Expand Up @@ -483,7 +483,7 @@ private boolean shouldTerminateTabularArray(String line, int lineDepth, int dept
* false otherwise.
*/
private boolean processTabularRow(String line, int lineDepth, int depth, List<String> keys,
String arrayDelimiter, List<Object> result) {
String arrayDelimiter, List<Object> result) {
if (lineDepth == depth + 1) {
String rowContent = line.substring((depth + 1) * options.indent());
Map<String, Object> row = parseTabularRow(rowContent, keys, arrayDelimiter);
Expand Down Expand Up @@ -655,9 +655,17 @@ private Object parseListItem(String content, int depth) {
String key = StringEscaper.unescape(itemContent.substring(0, colonIdx).trim());
String value = itemContent.substring(colonIdx + 1).trim();

currentLine++;

Map<String, Object> item = new LinkedHashMap<>();
// List item is at depth + 1, so pass depth + 1 to parseObjectItemValue
Object parsedValue = parseObjectItemValue(value, depth + 1);
Object parsedValue;
// If no next line exists, handle simple case
if (currentLine >= lines.length) {
parsedValue = value.trim().isEmpty() ? new LinkedHashMap<>() : PrimitiveDecoder.parse(value);
} else {
// List item is at depth + 1, so pass depth + 1 to parseObjectItemValue
parsedValue = parseObjectItemValue(value, depth + 1);
}
item.put(key, parsedValue);
parseListItemFields(item, depth);

Expand All @@ -668,20 +676,14 @@ private Object parseListItem(String content, int depth) {
* Parses the value portion of an object item in a list, handling nested
* objects,
* empty values, and primitives.
*
*
* @param value the value string to parse
* @param depth the depth of the list item
* @return the parsed value (Map, List, or primitive)
*/
private Object parseObjectItemValue(String value, int depth) {
currentLine++;
boolean isEmpty = value.trim().isEmpty();

// If no next line exists, handle simple case
if (currentLine >= lines.length) {
return isEmpty ? new LinkedHashMap<>() : PrimitiveDecoder.parse(value);
}

// Find next non-blank line and its depth
Integer nextDepth = findNextNonBlankLineDepth();
if (nextDepth == null) {
Expand All @@ -703,7 +705,7 @@ private Object parseObjectItemValue(String value, int depth) {

/**
* Finds the depth of the next non-blank line, skipping blank lines.
*
*
* @return the depth of the next non-blank line, or null if none exists
*/
private Integer findNextNonBlankLineDepth() {
Expand All @@ -721,7 +723,7 @@ private Integer findNextNonBlankLineDepth() {

/**
* Parses a field value, handling nested objects, empty values, and primitives.
*
*
* @param fieldValue the value string to parse
* @param fieldDepth the depth at which the field is located
* @return the parsed value (Map, List, or primitive)
Expand Down Expand Up @@ -758,7 +760,7 @@ private Object parseFieldValue(String fieldValue, int fieldDepth) {

/**
* Parses a keyed array field and adds it to the item map.
*
*
* @param fieldContent the field content to parse
* @param item the map to add the field to
* @param depth the depth of the list item
Expand Down Expand Up @@ -791,7 +793,7 @@ private boolean parseKeyedArrayField(String fieldContent, Map<String, Object> it

/**
* Parses a key-value field and adds it to the item map.
*
*
* @param fieldContent the field content to parse
* @param item the map to add the field to
* @param depth the depth of the list item
Expand Down Expand Up @@ -1107,7 +1109,7 @@ private void processDirectChildLine(Map<String, Object> result, String line, int
* Processes a keyed array line (e.g., "key[3]: value").
*/
private void processKeyedArrayLine(Map<String, Object> result, String content, Matcher keyedArray,
int parentDepth) {
int parentDepth) {
String originalKey = keyedArray.group(1).trim();
String key = StringEscaper.unescape(originalKey);
String arrayHeader = content.substring(keyedArray.group(1).length());
Expand Down Expand Up @@ -1238,7 +1240,7 @@ private void checkFinalValueConflict(String finalSegment, Object existing, Objec
/**
* Parses a key-value string into an Object, handling nested objects, empty
* values, and primitives.
*
*
* @param value the value string to parse
* @param depth the depth at which the key-value pair is located
* @return the parsed value (Map, List, or primitive)
Expand Down Expand Up @@ -1277,15 +1279,15 @@ private Object parseKeyValue(String value, int depth) {

/**
* Puts a key-value pair into a map, handling path expansion.
*
*
* @param map the map to put the key-value pair into
* @param originalKey the original key before being unescaped (used for path
* expansion check)
* @param unescapedKey the unescaped key
* @param value the value to put
*/
private void putKeyValueIntoMap(Map<String, Object> map, String originalKey, String unescapedKey,
Object value) {
Object value) {
// Handle path expansion
if (shouldExpandKey(originalKey)) {
expandPathIntoMap(map, unescapedKey, value);
Expand Down Expand Up @@ -1439,4 +1441,4 @@ private void validateIndentation(String line) {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static String encodePrimitive(JsonNode value, String delimiter) {
*/
private static String encodeNumber(JsonNode value) {
if (value.isIntegralNumber()) {
return value.asText();
return value.asString();
}

double doubleValue = value.asDouble();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
public final class StringValidator {
private static final Pattern NUMERIC_PATTERN = Pattern.compile("^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$",
Pattern.CASE_INSENSITIVE);
private static final Pattern OCTAL_PATTERN = Pattern.compile("^0\\d+$");
private static final Pattern OCTAL_PATTERN = Pattern.compile("^0[0-7]+$");
private static final Pattern UNQUOTED_KEY_PATTERN = Pattern.compile("^[A-Z_][\\w.]*$", Pattern.CASE_INSENSITIVE);
private static final Pattern STRUCTURAL_CHARS = Pattern.compile("[\\[\\]{}]");
private static final Pattern CONTROL_CHARS = Pattern.compile("[\\n\\r\\t]");
Expand Down Expand Up @@ -87,8 +87,7 @@ private static boolean looksLikeKeyword(String value) {
}

private static boolean looksLikeNumber(String value) {
return NUMERIC_PATTERN.matcher(value).matches()
|| OCTAL_PATTERN.matcher(value).matches();
return OCTAL_PATTERN.matcher(value).matches() || NUMERIC_PATTERN.matcher(value).matches();
}

private static boolean containsColon(String value) {
Expand Down
Loading
Loading