Skip to content

Add isEquivalentTo method to COSBase and COSObject - #712

Merged
MaximPlusov merged 4 commits into
integrationfrom
annex-j-comparison
Aug 5, 2026
Merged

Add isEquivalentTo method to COSBase and COSObject#712
MaximPlusov merged 4 commits into
integrationfrom
annex-j-comparison

Conversation

@LonelyMidoriya

@LonelyMidoriya LonelyMidoriya commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Add support for object comparison from ISO32000-2:2020 Annex J

Summary by CodeRabbit

  • New Features
    • Added comprehensive equivalence comparison for PDF objects, including arrays, dictionaries, streams, strings, names, booleans, nulls, indirect objects, and numeric values.
    • Numeric comparisons now recognize equivalent integer and decimal representations.
    • Stream comparisons use decoded content while ignoring non-semantic metadata.
    • String comparisons compare decoded values, including common PDF escape sequences and octal representations.
    • Comparisons safely handle nested structures, null values, and cyclic references.

@LonelyMidoriya LonelyMidoriya self-assigned this Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 306acc72-37dc-4fa5-b1e9-938d86452169

📥 Commits

Reviewing files that changed from the base of the PR and between ef2c95e and 239e12e.

📒 Files selected for processing (13)
  • src/main/java/org/verapdf/cos/COSArray.java
  • src/main/java/org/verapdf/cos/COSBase.java
  • src/main/java/org/verapdf/cos/COSBoolean.java
  • src/main/java/org/verapdf/cos/COSDictionary.java
  • src/main/java/org/verapdf/cos/COSIndirect.java
  • src/main/java/org/verapdf/cos/COSInteger.java
  • src/main/java/org/verapdf/cos/COSName.java
  • src/main/java/org/verapdf/cos/COSNull.java
  • src/main/java/org/verapdf/cos/COSNumber.java
  • src/main/java/org/verapdf/cos/COSObject.java
  • src/main/java/org/verapdf/cos/COSReal.java
  • src/main/java/org/verapdf/cos/COSStream.java
  • src/main/java/org/verapdf/cos/COSString.java
🚧 Files skipped from review as they are similar to previous changes (13)
  • src/main/java/org/verapdf/cos/COSNull.java
  • src/main/java/org/verapdf/cos/COSBoolean.java
  • src/main/java/org/verapdf/cos/COSNumber.java
  • src/main/java/org/verapdf/cos/COSBase.java
  • src/main/java/org/verapdf/cos/COSObject.java
  • src/main/java/org/verapdf/cos/COSReal.java
  • src/main/java/org/verapdf/cos/COSName.java
  • src/main/java/org/verapdf/cos/COSArray.java
  • src/main/java/org/verapdf/cos/COSStream.java
  • src/main/java/org/verapdf/cos/COSString.java
  • src/main/java/org/verapdf/cos/COSDictionary.java
  • src/main/java/org/verapdf/cos/COSIndirect.java
  • src/main/java/org/verapdf/cos/COSInteger.java

📝 Walkthrough

Walkthrough

The change adds recursive equivalence checking across COS arrays, dictionaries, streams, wrappers, and scalar types. Numeric values compare through BigDecimal, strings compare decoded values, and streams compare decoded content with selected dictionary entries.

Changes

COS equivalence

Layer / File(s) Summary
Scalar equivalence contracts
src/main/java/org/verapdf/cos/COSBase.java, src/main/java/org/verapdf/cos/COSNumber.java, src/main/java/org/verapdf/cos/COSInteger.java, src/main/java/org/verapdf/cos/COSReal.java, src/main/java/org/verapdf/cos/COSBoolean.java, src/main/java/org/verapdf/cos/COSNull.java, src/main/java/org/verapdf/cos/COSName.java
Scalar COS types implement equivalence. Numeric types expose decimal values for integer and real comparisons.
Wrapper and indirect dispatch
src/main/java/org/verapdf/cos/COSObject.java, src/main/java/org/verapdf/cos/COSIndirect.java
Wrapped and indirect values delegate equivalence checks to their direct COS values.
Array and dictionary recursion
src/main/java/org/verapdf/cos/COSArray.java, src/main/java/org/verapdf/cos/COSDictionary.java
Arrays compare ordered elements. Dictionaries compare non-null keys and values recursively with cycle tracking.
Stream decoding and string equivalence
src/main/java/org/verapdf/cos/COSStream.java, src/main/java/org/verapdf/cos/COSString.java
Streams compare decoded bytes and non-excluded dictionary entries. Strings compare decoded string values.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant COSArray
  participant COSDictionary
  participant COSStream
  participant COSBasePair
  COSArray->>COSBasePair: Check and record compared pairs
  COSArray->>COSDictionary: Compare nested COSBase values
  COSDictionary->>COSBasePair: Check and record compared pairs
  COSDictionary->>COSStream: Compare nested stream values
  COSStream->>COSBasePair: Check and record compared pairs
  COSStream->>COSDictionary: Compare decoded bytes and dictionary entries
Loading

Suggested reviewers: maximplusov

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the API addition but omits the broader implementations across COS types.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch annex-j-comparison

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (1)
src/main/java/org/verapdf/cos/COSDictionary.java (1)

302-311: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stray/inaccurate comment.

// assume getKey returns a COSBase wrapper is confusing — getKey returns a COSObject (the wrapper), and .get() on it yields the COSBase. Looks like a leftover authoring note.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/verapdf/cos/COSDictionary.java` around lines 302 - 311,
Remove the inaccurate authoring comment from getNonNullKeySet while preserving
the existing getKey(key).get() value retrieval and null-filtering behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/verapdf/cos/COSBase.java`:
- Around line 145-147: Preserve the supplied cycle-detection state during
equivalent comparisons: update COSBase.isEquivalentTo(Object, List<COSBasePair>)
to dispatch recursive comparisons with the existing checkedObjects list, and
update both COSIndirect comparison paths at
src/main/java/org/verapdf/cos/COSIndirect.java lines 483-486 and 492-497 to pass
that same list when comparing indirect targets, including COSObject targets; do
not restart traversal through one-argument methods.

In `@src/main/java/org/verapdf/cos/COSDictionary.java`:
- Around line 313-335: Update both overloads of COSDictionary.isEquivalentTo,
including the recursive variant, to reject COSStream and any other COSDictionary
subclass before comparing entries; preserve COSObject unwrapping and allow only
exact COSDictionary instances through the dictionary comparison path.

In `@src/main/java/org/verapdf/cos/COSName.java`:
- Around line 95-107: Update COSName.isEquivalentTo to handle null ASAtom values
before calling getValue() or converting to bytes. Ensure two null values compare
as equivalent, a null versus non-null value returns false, and retain the
existing byte comparison for non-null values.

In `@src/main/java/org/verapdf/cos/COSObject.java`:
- Around line 497-505: Update COSObject.isEquivalentTo in
src/main/java/org/verapdf/cos/COSObject.java:497-505 to handle null base values
before delegating, preserving equivalent empty-wrapper behavior. Also update
COSIndirect.isEquivalentTo in
src/main/java/org/verapdf/cos/COSIndirect.java:487-494 to check getDirect() for
null or unresolved results before accessing base, and define equivalence
consistently without dereferencing null objects.

In `@src/main/java/org/verapdf/cos/COSReal.java`:
- Around line 49-52: Ensure non-finite values accepted by
COSReal.setValue(double) cannot reach BigDecimal conversion: update COSReal’s
set/setReal/construct paths to reject NaN and infinities, or explicitly handle
them in COSReal.getDecimalValue() before BigDecimal.valueOf(value). Also update
COSInteger.isEquivalentTo(...) to handle non-finite COSReal values without
invoking BigDecimal conversion; apply the changes in
src/main/java/org/verapdf/cos/COSReal.java lines 49-52 and
src/main/java/org/verapdf/cos/COSInteger.java lines 88-90.

In `@src/main/java/org/verapdf/cos/COSStream.java`:
- Around line 320-329: Update the decoded-stream comparison block in
COSStream.equals to manage both getData(FilterFlags.DECODE) results with
try-with-resources, ensuring thisDecoded and thatDecoded are closed on success
and IOException while preserving the existing equalsDecodedStreams and failure
behavior.
- Around line 320-329: Update the decoded-stream comparison block in
COSStream.equals(Object, List) to guard against either this.stream or
that.stream being null before calling getData(). Return false for the
null-stream case, matching the existing stream checks in equals(Object, List),
while preserving the current decoded comparison and IOException handling for
non-null streams.

In `@src/main/java/org/verapdf/cos/COSString.java`:
- Around line 437-448: Update COSString.isEquivalentTo(Object) to unwrap a
COSObject argument before checking for COSString and comparing canonical bytes,
matching the established behavior of COSStream, COSDictionary, and COSArray.
Preserve identity handling and return false for unsupported underlying types.
- Around line 383-398: Fix octal escape handling in the parsing loop around the
octal accumulation logic so the outer iteration advances past the final consumed
octal digit before continue, preventing it from being emitted again as a
literal. Preserve correct one- to three-digit octal decoding, and add unit
coverage for escapes such as \101 and \7 through isEquivalentTo or
getCanonicalBytes.

---

Nitpick comments:
In `@src/main/java/org/verapdf/cos/COSDictionary.java`:
- Around line 302-311: Remove the inaccurate authoring comment from
getNonNullKeySet while preserving the existing getKey(key).get() value retrieval
and null-filtering behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 168acea3-5e14-4e08-ab69-3fe3b6814d78

📥 Commits

Reviewing files that changed from the base of the PR and between 2f315b8 and fdb96de.

📒 Files selected for processing (13)
  • src/main/java/org/verapdf/cos/COSArray.java
  • src/main/java/org/verapdf/cos/COSBase.java
  • src/main/java/org/verapdf/cos/COSBoolean.java
  • src/main/java/org/verapdf/cos/COSDictionary.java
  • src/main/java/org/verapdf/cos/COSIndirect.java
  • src/main/java/org/verapdf/cos/COSInteger.java
  • src/main/java/org/verapdf/cos/COSName.java
  • src/main/java/org/verapdf/cos/COSNull.java
  • src/main/java/org/verapdf/cos/COSNumber.java
  • src/main/java/org/verapdf/cos/COSObject.java
  • src/main/java/org/verapdf/cos/COSReal.java
  • src/main/java/org/verapdf/cos/COSStream.java
  • src/main/java/org/verapdf/cos/COSString.java

Comment thread src/main/java/org/verapdf/cos/COSBase.java Outdated
Comment thread src/main/java/org/verapdf/cos/COSDictionary.java
Comment thread src/main/java/org/verapdf/cos/COSName.java
Comment thread src/main/java/org/verapdf/cos/COSObject.java Outdated
Comment thread src/main/java/org/verapdf/cos/COSReal.java
Comment thread src/main/java/org/verapdf/cos/COSStream.java Outdated
Comment thread src/main/java/org/verapdf/cos/COSString.java Outdated
Comment thread src/main/java/org/verapdf/cos/COSString.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/verapdf/cos/COSInteger.java`:
- Around line 81-85: Update isEquivalentTo in
src/main/java/org/verapdf/cos/COSInteger.java lines 81-85, COSBoolean.java lines
75-79, COSName.java lines 96-100, and COSString.java lines 369-373 to return
false when the operand is null before calling isIndirect(); also guard the
dereferenced result so unresolved indirect operands return false instead of
throwing.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e31f0423-195f-4a18-88d9-dd00fddb2270

📥 Commits

Reviewing files that changed from the base of the PR and between fdb96de and 41cb5af.

📒 Files selected for processing (12)
  • src/main/java/org/verapdf/cos/COSArray.java
  • src/main/java/org/verapdf/cos/COSBase.java
  • src/main/java/org/verapdf/cos/COSBoolean.java
  • src/main/java/org/verapdf/cos/COSDictionary.java
  • src/main/java/org/verapdf/cos/COSIndirect.java
  • src/main/java/org/verapdf/cos/COSInteger.java
  • src/main/java/org/verapdf/cos/COSName.java
  • src/main/java/org/verapdf/cos/COSNull.java
  • src/main/java/org/verapdf/cos/COSObject.java
  • src/main/java/org/verapdf/cos/COSReal.java
  • src/main/java/org/verapdf/cos/COSStream.java
  • src/main/java/org/verapdf/cos/COSString.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/org/verapdf/cos/COSStream.java
  • src/main/java/org/verapdf/cos/COSNull.java
  • src/main/java/org/verapdf/cos/COSReal.java

Comment thread src/main/java/org/verapdf/cos/COSInteger.java
@MaximPlusov
MaximPlusov self-requested a review August 5, 2026 09:42
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@MaximPlusov
MaximPlusov merged commit 39f3a05 into integration Aug 5, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants