|
7 | 7 | - Follow the sequence plan → implement → verify; do not pivot without restating the plan. |
8 | 8 | - Stop immediately on unexpected failures and ask before changing approach. |
9 | 9 | - Keep edits atomic and avoid leaving mixed partial states. |
10 | | -- Propose options with trade-offs before invasive changes. |
| 10 | +- Propose jsonSchemaOptions with trade-offs before invasive changes. |
11 | 11 | - Prefer mechanical, reversible transforms (especially when syncing upstream sources). |
12 | 12 | - Validate that outputs are non-empty before overwriting files. |
13 | 13 | - Minimal shims are acceptable only when needed to keep backports compiling. |
@@ -287,7 +287,7 @@ git push -u origin "rel-$VERSION" && echo "✅ Success" || echo "🛑 Unable to |
287 | 287 | 2. **MVF Flow (Mermaid)** |
288 | 288 | ```mermaid |
289 | 289 | flowchart TD |
290 | | - A[compile(initialDoc, initialUri, options)] --> B[Work Stack (LIFO)] |
| 290 | + A[compile(initialDoc, initialUri, jsonSchemaOptions)] --> B[Work Stack (LIFO)] |
291 | 291 | B -->|push initialUri| C{pop docUri} |
292 | 292 | C -->|empty| Z[freeze Roots (immutable) → return primary root facade] |
293 | 293 | C --> D[fetch/parse JSON for docUri] |
@@ -334,7 +334,7 @@ type RefToken = |
334 | 334 | | { kind: "Local"; pointer: JsonPointer } |
335 | 335 | | { kind: "Remote"; doc: DocURI; pointer: JsonPointer }; |
336 | 336 |
|
337 | | -function compile(initialDoc: unknown, initialUri: DocURI, options?: unknown): { |
| 337 | +function compile(initialDoc: unknown, initialUri: DocURI, jsonSchemaOptions?: unknown): { |
338 | 338 | primary: Root; |
339 | 339 | roots: Roots; // unused by MVF runtime; ready for remote expansions |
340 | 340 | } { |
@@ -419,3 +419,137 @@ def xform(text): |
419 | 419 | print('OK') |
420 | 420 | PY |
421 | 421 | ``` |
| 422 | + |
| 423 | +# Java DOP Coding Standards #################### |
| 424 | + |
| 425 | +This file is a Gen AI summary of CODING_STYLE.md to use less tokens of context window. Read the original file for full details. |
| 426 | + |
| 427 | +IMPORTANT: We do TDD so all code must include targeted unit tests. |
| 428 | +IMPORTANT: Never disable tests written for logic that we are yet to write we do Red-Green-Refactor coding. |
| 429 | + |
| 430 | +## Core Principles |
| 431 | + |
| 432 | +* Use Records for all data structures. Use sealed interfaces for protocols. |
| 433 | +* Prefer static methods with Records as parameters |
| 434 | +* Default to package-private scope |
| 435 | +* Package-by-feature, not package-by-layer |
| 436 | +* Create fewer, cohesive, wide packages (functionality modules or records as protocols) |
| 437 | +* Use public only when cross-package access is required |
| 438 | +* Use JEP 467 Markdown documentation examples: `/// good markdown` not legacy `/** bad html */` |
| 439 | +* Apply Data-Oriented Programming principles and avoid OOP |
| 440 | +* Use Stream operations instead of traditional loops. Never use `for(;;)` with mutable loop variables use |
| 441 | + `Arrays.setAll` |
| 442 | +* Prefer exhaustive destructuring switch expressions over if-else statements |
| 443 | +* Use destructuring switch expressions that operate on Records and sealed interfaces |
| 444 | +* Use anonymous variables in record destructuring and switch expressions |
| 445 | +* Use `final var` for local variables, parameters, and destructured fields |
| 446 | +* Apply JEP 371 "Local Classes and Interfaces" for cohesive files with narrow APIs |
| 447 | + |
| 448 | +## Data-Oriented Programming |
| 449 | + |
| 450 | +* Separate data (immutable Records) from behavior (never utility classes always static methods) |
| 451 | +* Use immutable generic data structures (maps, lists, sets) and take defense copies in constructors |
| 452 | +* Write pure functions that don't modify state |
| 453 | +* Leverage Java 21+ features: |
| 454 | + * Records for immutable data |
| 455 | + * Pattern matching for structural decomposition |
| 456 | + * Sealed classes for exhaustive switches |
| 457 | + * Virtual threads for concurrent processing |
| 458 | + |
| 459 | +## Package Structure |
| 460 | + |
| 461 | +* Use default (package-private) access as the standard. Do not use 'private' or 'public' by default. |
| 462 | +* Limit public to genuine cross-package APIs |
| 463 | +* Prefer package-private static methods. Do not use 'private' or 'public' by default. |
| 464 | +* Limit private to security-related code |
| 465 | +* Avoid anti-patterns: boilerplate OOP, excessive layering, dependency injection overuse |
| 466 | + |
| 467 | +## Constants and Magic Numbers |
| 468 | + |
| 469 | +* **NEVER use magic numbers** - always use enum constants |
| 470 | +* **NEVER write large if-else-if statements over known types** - will not be exhaustive and creates bugs when new types are added. Use exhaustive switch statements over bounded sets such as enum values or sealed interface permits |
| 471 | + |
| 472 | +## Functional Style |
| 473 | + |
| 474 | +* Combine Records + static methods for functional programming |
| 475 | +* Emphasize immutability and explicit state transformations |
| 476 | +* Reduce package count to improve testability |
| 477 | +* Implement Algebraic Data Types pattern with Function Modules |
| 478 | +* Modern Stream Programming |
| 479 | +* Use Stream API instead of traditional loops |
| 480 | +* Write declarative rather than imperative code |
| 481 | +* Chain operations without intermediate variables |
| 482 | +* Support immutability throughout processing |
| 483 | +* Example: `IntStream.range(0, 100).filter(i -> i % 2 == 0).sum()` instead of counting loops |
| 484 | +* Always use final variables in functional style. |
| 485 | +* Prefer `final var` with self documenting names over `int i` or `String s` but its not possible to do that on a `final` variable that is not yet initialized so its a weak preference not a strong one. |
| 486 | +* Avoid just adding new functionality to the top of a method to make an early return. It is fine to have a simple guard statement. Yet general you should pattern match over the input to do different things with the same method. Adding special case logic is a code smell that should be avoided. |
| 487 | + |
| 488 | +## Documentation using JEP 467 Markdown documentation |
| 489 | + |
| 490 | +IMPORTANT: You must not write JavaDoc comments that start with `/**` and end with `*/` |
| 491 | +IMPORTANT: You must "JEP 467: Markdown Documentation Comments" that start all lines with `///` |
| 492 | + |
| 493 | +Here is an example of the correct format for documentation comments: |
| 494 | + |
| 495 | +```java |
| 496 | +/// Returns a hash code value for the object. This method is |
| 497 | +/// supported for the benefit of hash tables such as those provided by |
| 498 | +/// [java.util.HashMap]. |
| 499 | +/// |
| 500 | +/// The general contract of `hashCode` is: |
| 501 | +/// |
| 502 | +/// - Whenever it is invoked on the same object more than once during |
| 503 | +/// an execution of a Java application, the `hashCode` method |
| 504 | +/// - If two objects are equal according to the |
| 505 | +/// [equals][#equals(Object)] method, then calling the |
| 506 | +/// - It is _not_ required that if two objects are unequal |
| 507 | +/// according to the [equals][#equals(Object)] method, then |
| 508 | +/// |
| 509 | +/// @return a hash code value for this object. |
| 510 | +/// @see java.lang.Object#equals(java.lang.Object) |
| 511 | +``` |
| 512 | + |
| 513 | +## Logging |
| 514 | + |
| 515 | +- Use Java's built-in logging: `java.util.logging.Logger` |
| 516 | +- Log levels: Use appropriate levels (FINE, FINER, INFO, WARNING, SEVERE) |
| 517 | + - **FINE**: Production-level debugging, default for most debug output |
| 518 | + - **FINER**: Verbose debugging, detailed internal flow, class resolution details |
| 519 | + - **INFO**: Important runtime information |
| 520 | +- LOGGER is a static field: `static final Logger LOGGER = Logger.getLogger(ClassName.class.getName());` where use the primary interface or the package as the logger name with the logger package-private and shared across the classes when the package is small enough. |
| 521 | +- Use lambda logging for performance: `LOGGER.fine(() -> "message " + variable);` |
| 522 | + |
| 523 | +# Compile, Test, Debug Loop |
| 524 | + |
| 525 | +- **Check Compiles**: Focusing on the correct mvn module run without verbose logging and do not grep the output to see compile errors: |
| 526 | + ```bash |
| 527 | + $(command -v mvnd || command -v mvn || command -v ./mvnw) -pl json-java21-api-tracker -Djava.util.logging.ConsoleHandler.level=SEVERE |
| 528 | + ``` |
| 529 | +- **Debug with Verbose Logs**: Use `-Dtest=` to focus on just one or two test methods, or one class, using more logging to debug the code: |
| 530 | + ```bash |
| 531 | + $(command -v mvnd || command -v mvn || command -v ./mvnw) -pl json-java21-api-tracker -Dtest=XXX -Djava.util.logging.ConsoleHandler.level=FINER |
| 532 | + ``` |
| 533 | +- **No Grep Filtering**: Use logging levels to filter output, do not grep the output for compile errors, just run less test methods with the correct logging to reduce the output to a manageable size. Filtering hides problems and needs more test excution to find the same problems which wastes time. |
| 534 | + |
| 535 | +## Modern Java Singleton Pattern: Sealed Interfaces |
| 536 | + |
| 537 | +**Singleton Object Anti-Pattern**: Traditional singleton classes with private constructors and static instances are legacy should be avoided. With a functional style we can create a "package-private companion module" of small package-private methods with `sealed interfacee GoodSingletonModule permits Nothing { enum Nothing extends GoodSingletonModule{}; /* static functional methods here */ }`. |
| 538 | + |
| 539 | +### Assertions and Input Validation |
| 540 | + |
| 541 | +1. On the public API entry points use `Objects.assertNonNull()` to ensure that the inputs are legal. |
| 542 | +2. After that on internal method that should be passed only valid data use `assert` to ensure that the data is valid. |
| 543 | +- e.g. use `assert x==y: "unexpected x="+x+" y="+y;` as `mvn` base should be run with `-ea` to enable assertions. |
| 544 | +3. Often there is an `orElseThrow()` which can be used so the only reason to use `assert` is to add more logging to the error message. |
| 545 | +4. Consider using the validations of `Object` and `Arrays` and the like to ensure that the data is valid. |
| 546 | +- e.g. `Objects.requireNonNull(type, "type must not be null")` or `Arrays.checkIndex(index, array.length)`. |
| 547 | + |
| 548 | +## JEP References |
| 549 | + |
| 550 | +[JEP 467](https://openjdk.org/jeps/467): Markdown Documentation in JavaDoc |
| 551 | +[JEP 371](https://openjdk.org/jeps/371): Local Classes and Interfaces |
| 552 | +[JEP 395](https://openjdk.org/jeps/395): Records |
| 553 | +[JEP 409](https://openjdk.org/jeps/409): Sealed Classes |
| 554 | +[JEP 440](https://openjdk.org/jeps/440): Record Patterns |
| 555 | +[JEP 427](https://openjdk.org/jeps/427): Pattern Matching for Switch |
0 commit comments