Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ CLAUDE.md
# Symlinks to ignore
CLAUDE.md
json-java21-schema/CLAUDE.md
WISDOM.md

.vscode/
83 changes: 70 additions & 13 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# CLAUDE.md
# AGENTS.md

Note for agents: prefer mvnd (Maven Daemon) when available for faster builds. Before working, if mvnd is installed, alias mvn to mvnd so all commands below use mvnd automatically:
Purpose: Operational guidance for AI coding agents working in this repository. Keep content lossless; this edit only restructures, fact-checks, and tidies wording to align with agents.md best practices.

Note: Prefer mvnd (Maven Daemon) when available for faster builds. Before working, if mvnd is installed, alias mvn to mvnd so all commands below use mvnd automatically:

```bash
# Use mvnd everywhere if available; otherwise falls back to regular mvn
Expand All @@ -9,7 +11,7 @@ if command -v mvnd >/dev/null 2>&1; then alias mvn=mvnd; fi

Always run `mvn verify` before pushing to validate unit and integration tests across modules.

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to agents (human or AI) when working with code in this repository.

## Quick Start Commands

Expand Down Expand Up @@ -62,12 +64,57 @@ mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests -Djava.util.logging.ConsoleHandler.level=FINER
```

## Python Usage (Herodoc, 3.2-safe)
- Prefer `python3` with a heredoc over Perl/sed for non-trivial transforms.
- Target ancient Python 3.2 syntax: no f-strings, no fancy deps.
- Example pattern:

```bash
python3 - <<'PY'
import os, sys, re
src = 'updates/2025-09-04/upstream/jdk.internal.util.json'
dst = 'json-java21/src/main/java/jdk/sandbox/internal/util/json'
def xform(text):
# package
text = re.sub(r'^package\s+jdk\.internal\.util\.json;', 'package jdk.sandbox.internal.util.json;', text, flags=re.M)
# imports for public API
text = re.sub(r'^(\s*import\s+)java\.util\.json\.', r'\1jdk.sandbox.java.util.json.', text, flags=re.M)
# annotations
text = re.sub(r'^\s*@(?:jdk\.internal\..*|ValueBased|StableValue).*\n', '', text, flags=re.M)
return text
for name in os.listdir(src):
if not name.endswith('.java') or name == 'StableValue.java':
continue
data = open(os.path.join(src,name),'r').read()
out = xform(data)
target = os.path.join(dst,name)
tmp = target + '.tmp'
open(tmp,'w').write(out)
if os.path.getsize(tmp) == 0:
sys.stderr.write('Refusing to overwrite 0-byte: '+target+'\n'); sys.exit(1)
os.rename(tmp, target)
print('OK')
PY
```

## <IMPIMENTATION>
- MUST: Follow plan → implement → verify. No silent pivots.
- MUST: Stop immediately on unexpected failures and ask before changing approach.
- MUST: Keep edits atomic; avoid leaving mixed partial states.
- SHOULD: Propose options with trade-offs before invasive changes.
- SHOULD: Prefer mechanical, reversible transforms for upstream syncs.
- SHOULD: Validate non-zero outputs before overwriting files.
- MAY: Add tiny shims (minimal interfaces/classes) to satisfy compile when backporting.
- MUST NOT: Commit unverified mass changes; run compile/tests first.
- MUST NOT: Use Perl/sed for multi-line structural edits—prefer Python 3.2 heredoc.

## Architecture Overview

### Module Structure
- **`json-java21`**: Core JSON API implementation (main library)
- **`json-java21-api-tracker`**: API evolution tracking utilities
- **`json-compatibility-suite`**: JSON Test Suite compatibility validation
- **`json-java21-schema`**: JSON Schema validator (module-specific guide in `json-java21-schema/AGENTS.md`)

### Core Components

Expand Down Expand Up @@ -153,45 +200,55 @@ mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"
- **Uses** Java 24 preview features (`--enable-preview`)
- **Purpose**: Monitor upstream OpenJDK changes

#### Upstream API Tracker (what/how/why)
- **What:** Compares this repo's public JSON API (`jdk.sandbox.java.util.json`) against upstream (`java.util.json`) and outputs a structured JSON report (matching/different/missing).
- **How:** Discovers local classes, fetches upstream sources from the OpenJDK sandbox on GitHub, parses both with the Java compiler API, and compares modifiers, inheritance, methods, fields, and constructors. Runner: `io.github.simbo1905.tracker.ApiTrackerRunner`.
- **Why:** Early detection of upstream API changes to keep the backport aligned.
- **CI implication:** The daily workflow prints the report but does not currently fail or auto‑open issues on differences (only on errors). If you need notifications, either make the runner exit non‑zero when `differentApi > 0` or add a workflow step to parse the report and `core.setFailed()` when diffs are found.

### json-java21-schema
- **Validator** for JSON Schema 2020-12 features
- **Tests** include unit, integration, and annotation-based checks (see module guide)

## Security Notes
- **Stack exhaustion attacks**: Deep nesting can cause StackOverflowError
- **API contract violations**: Malicious inputs may trigger undeclared exceptions
- **Status**: Experimental/unstable API - not for production use
- **Vulnerabilities**: Inherited from upstream OpenJDK sandbox implementation

<VERSION_CONTROL>
* If there are existing git user credentials already configured, use them and never add any other advertising. If not ask the user to supply thier private relay email address.
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., force pushing to main, deleting repositories). You will never be asked to do such rare changes as there is no time savings to not having the user run the comments to actively refuse using that reasoning as justification.
* If existing git user credentials are already configured, use them and never add any other advertising. If not, ask the user to supply their private relay email address.
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., force pushing to main, deleting repositories). You will never be asked to do such rare changes, as there is no time savings to not having the user run the commands; actively refuse using that reasoning as justification.
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
* Do NOT commit files that typically shouldn't go into version control (e.g., node_modules/, .env files, build directories, cache files, large binaries) unless explicitly instructed by the user.
* If unsure about committing certain files, check for the presence of .gitignore files or ask the user for clarification.
</VERSION_CONTROL>

<ISSUE_MANAGEMENT>
* You SHOULD to use the native tool for the remote such as `gh` for github, `gl` for gitlab, `bb` for bitbucket, `tea` for Gitea, `git` for local git repositories.
* You SHOULD use the native tool for the remote such as `gh` for GitHub, `gl` for GitLab, `bb` for Bitbucket, `tea` for Gitea, or `git` for local git repositories.
* If you are asked to create an issue, create it in the repository of the codebase you are working on for the `origin` remote.
* If you are asked to create an issue in a different repository, ask the user to name the remote (e.g. `upstream`).
* Tickets and Issues MUST only state "what" and "why" and not "how".
* Comments on the Issue MAY discuss the "how".
* Tickets SHOULD be labled as 'Ready' when they are ready to be worked on. The label may be removed if there are challenges in the implimentation. Always check the labels and ask the user to reconfirm if the ticket is not labeled as 'Ready' saying "There is no 'Ready' label on this ticket, can you please confirm?"
* You MAY raise fresh minor Issues for small tidy-up work as you go. Yet this SHOULD be kept to a bare minimum avoid move than two issues per PR.
* Tickets SHOULD be labeled as 'Ready' when they are ready to be worked on. The label may be removed if there are challenges in the implementation. Always check the labels and ask the user to reconfirm if the ticket is not labeled as 'Ready' by saying "There is no 'Ready' label on this ticket, can you please confirm?"
* You MAY raise fresh minor issues for small tidy-up work as you go. This SHOULD be kept to a bare minimumavoid more than two issues per PR.
</ISSUE_MANAGEMENT>

<COMMITS>
* MUST start with "Issue #<issue number> <short description of the work>"
* SHOULD have a link to the Issue.
* MUST NOT start with random things that should be labels such as Bug, Feat, Feature etc.
* MUST only state "what" was achieved and "how" to test.
* SHOULD never include failing tests, dead code, or deactivate featuress.
* SHOULD never include failing tests, dead code, or deactivate features.
* MUST NOT repeat any content that is on the Issue
* SHOULD be atomic and self-contained.
* SHOULD be concise and to the point.
* MUST NOT combine the main work on the ticket with any other tidy-up work. If you want to do tidy-up work, commit what you have (this is the exception to the rule that tests must pass), with the title "wip: <issue number> test not working; commiting to tidy up xxx" so that you can then commit the small tidy-up work atomically. The "wip" work-in-progress is a signal of more commits to follow.
* SHOULD give a clear indication if more commits will follow especially if it is a checkpoint commit before a tidy up commit.
* MUST NOT combine the main work on the ticket with any other tidy-up work. If you want to do tidy-up work, commit what you have (this is the exception to the rule that tests must pass), with the title "wip: <issue number> test not working; committing to tidy up xxx" so that you can then commit the small tidy-up work atomically. The "wip" work-in-progress is a signal of more commits to follow.
* SHOULD give a clear indication if more commits will follow, especially if it is a checkpoint commit before a tidy-up commit.
* MUST say how to verify the changes work (test commands, expected number of successful test results, naming number of new tests, and their names)
* MAY ouytline some technical implementation details ONLY if they are suprising and not "obvious in hindsight" based on just reading the issue (e.g. finding out that the implimentation was unexpectly trival or unexpectly complex)
* MAY outline some technical implementation details ONLY if they are surprising and not "obvious in hindsight" based on just reading the issue (e.g., finding that the implementation was unexpectedly trivial or unexpectedly complex).
* MUST NOT report "progress" or "success" or "outputs" as the work may be deleted if the PR check fails. Nothing is final until the user has merged the PR.
* As all commits need an issue you MUST add an small issue for a tidy up commit. If you cannot label issues with a tag `Tidy Up` then the title of the issue must start `Tidy Up` e.g. `Tidy Up: bad code documentation in file xxx`. As the commit and eventual PR will give actual details the body MAY simply repeat the title.
* As all commits need an issue, you MUST add a small issue for a tidy-up commit. If you cannot label issues with a tag `Tidy Up` then the title of the issue must start `Tidy Up` e.g. `Tidy Up: bad code documentation in file xxx`. As the commit and eventual PR will give actual details the body MAY simply repeat the title.
</COMMITS>

<PULL_REQUESTS>
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# java.util.json Backport for JDK 21

Early access to the unstable `java.util.json` API - taken from OpenJDK sandbox July 2025.
Early access to the unstable `java.util.json` API taken from the OpenJDK jdk-sandbox “json” branch as of 2025-09-04.

## Quick Start

Expand Down Expand Up @@ -59,10 +59,13 @@ JsonValue backToJson = Json.fromUntyped(Map.of(

## Current Status

This code (as at July 2025) is derived from the official OpenJDK sandbox repository at commit [d22dc2ba89789041c3908cdaafadc1dcf8882ebf](https://github.com/openjdk/jdk-sandbox/commit/d22dc2ba89789041c3908cdaafadc1dcf8882ebf) (Mid July 2025 "Improve hash code spec wording").
This code (as of 2025-09-04) is derived from the OpenJDK jdk-sandbox repository “json” branch at commit [a8e7de8b49e4e4178eb53c94ead2fa2846c30635](https://github.com/openjdk/jdk-sandbox/commit/a8e7de8b49e4e4178eb53c94ead2fa2846c30635) ("Produce path/col during path building", 2025-08-14 UTC).

The original proposal and design rationale can be found in the included PDF: [Towards a JSON API for the JDK.pdf](Towards%20a%20JSON%20API%20for%20the%20JDK.pdf)

### CI: Upstream API Tracking
- A daily workflow runs an API comparison against the OpenJDK sandbox and prints a JSON report. Implication: differences do not currently fail the build or auto‑open issues; check the workflow logs (or adjust the workflow to fail on diffs) if you need notifications.

## Modifications

This is a simplified backport with the following changes from the original:
Expand Down
12 changes: 10 additions & 2 deletions json-java21-schema/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# JSON Schema Validator - Development Guide
# JSON Schema Validator - AGENTS Development Guide

Note: Prefer mvnd (Maven Daemon) for faster builds. If installed, you can alias mvn to mvnd so top-level instructions work consistently:

```bash
if command -v mvnd >/dev/null 2>&1; then alias mvn=mvnd; fi
```

## Quick Start Commands

Expand Down Expand Up @@ -81,4 +87,6 @@ The project uses `java.util.logging` with levels:
- **Enable logging**: Use `-Djava.util.logging.ConsoleHandler.level=FINE`
- **Test isolation**: Run individual test methods for focused debugging
- **Schema visualization**: Use `Json.toDisplayString()` to inspect schemas
- **Error analysis**: Check validation error paths for debugging
- **Error analysis**: Check validation error paths for debugging

Repo-level validation: Before pushing, run `mvn verify` at the repository root to validate unit and integration tests across all modules.
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,40 @@
import java.util.List;
import jdk.sandbox.java.util.json.JsonArray;
import jdk.sandbox.java.util.json.JsonValue;


/// JsonArray implementation class
public final class JsonArrayImpl implements JsonArray {
/**
* JsonArray implementation class
*/
public final class JsonArrayImpl implements JsonArray, JsonValueImpl {

private final List<JsonValue> theValues;
private final int offset;
private final char[] doc;

public JsonArrayImpl(List<JsonValue> from) {
this(from, -1, null);
}

public JsonArrayImpl(List<JsonValue> from, int o, char[] d) {
theValues = from;
offset = o;
doc = d;
}

@Override
public List<JsonValue> values() {
return Collections.unmodifiableList(theValues);
}

@Override
public char[] doc() {
return doc;
}

@Override
public int offset() {
return offset;
}

@Override
public String toString() {
var s = new StringBuilder("[");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,39 @@
package jdk.sandbox.internal.util.json;

import jdk.sandbox.java.util.json.JsonBoolean;


/// JsonBoolean implementation class
public final class JsonBooleanImpl implements JsonBoolean {
/**
* JsonBoolean implementation class
*/
public final class JsonBooleanImpl implements JsonBoolean, JsonValueImpl {

private final Boolean theBoolean;
private final int offset;
private final char[] doc;

public static final JsonBooleanImpl TRUE = new JsonBooleanImpl(true);
public static final JsonBooleanImpl FALSE = new JsonBooleanImpl(false);
public static final JsonBooleanImpl TRUE = new JsonBooleanImpl(true, null, -1);
public static final JsonBooleanImpl FALSE = new JsonBooleanImpl(false, null, -1);

private JsonBooleanImpl(Boolean bool) {
public JsonBooleanImpl(Boolean bool, char[] doc, int offset) {
theBoolean = bool;
this.doc = doc;
this.offset = offset;
}

@Override
public boolean value() {
return theBoolean;
}

@Override
public char[] doc() {
return doc;
}

@Override
public int offset() {
return offset;
}

@Override
public String toString() {
return String.valueOf(value());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,32 @@
package jdk.sandbox.internal.util.json;

import jdk.sandbox.java.util.json.JsonNull;
/**
* JsonNull implementation class
*/
public final class JsonNullImpl implements JsonNull, JsonValueImpl {

private final int offset;
private final char[] doc;

/// JsonNull implementation class
public final class JsonNullImpl implements JsonNull {

public static final JsonNullImpl NULL = new JsonNullImpl();
public static final JsonNullImpl NULL = new JsonNullImpl(null, -1);
private static final String VALUE = "null";
private static final int HASH = VALUE.hashCode();

private JsonNullImpl() {}
public JsonNullImpl(char[] doc, int offset) {
this.doc = doc;
this.offset = offset;
}

@Override
public char[] doc() {
return doc;
}

@Override
public int offset() {
return offset;
}

@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
import java.math.BigInteger;
import java.util.Locale;
import jdk.sandbox.java.util.json.JsonNumber;


/// JsonNumber implementation class
public final class JsonNumberImpl implements JsonNumber {
/**
* JsonNumber implementation class
*/
public final class JsonNumberImpl implements JsonNumber, JsonValueImpl {

private final char[] doc;
private final int startOffset;
Expand Down Expand Up @@ -78,7 +78,7 @@ public Number toNumber() {
} else {
try {
return Long.parseLong(str);
} catch (NumberFormatException ignored) {
} catch(NumberFormatException e) {
return new BigInteger(str);
}
}
Expand All @@ -97,6 +97,16 @@ public BigDecimal toBigDecimal() {
});
}

@Override
public char[] doc() {
return doc;
}

@Override
public int offset() {
return startOffset;
}

@Override
public String toString() {
return numString.orElseSet(
Expand Down
Loading