Skip to content

Write the entry identifier as a data item, not only as the block name - #1144

Open
aalhossary wants to merge 1 commit into
biojava:masterfrom
aalhossary:aa/emit-entry-id
Open

Write the entry identifier as a data item, not only as the block name#1144
aalhossary wants to merge 1 commit into
biojava:masterfrom
aalhossary:aa/emit-entry-id

Conversation

@aalhossary

Copy link
Copy Markdown
Member

Fixes #1143

The problem

AbstractCifFileSupplier wrote the PDB identifier only as the name of the data_ block, and emitted neither _entry.id nor _struct.entry_id. Consumers read the identifier as a data item, not from the block header, so it did not survive being written:

  • BioJava's own CifStructureConsumerImpl.consumeStruct reads _struct.entry_id, so Structure.getPdbId() came back null after a toText() / fromInputStream() round trip.
  • Jmol's mmCIF reader takes _M.pdbID from _entry.id, so a structure handed over with openStringInline(structure.toMMCIF()) arrived with no identifier — which in turn breaks anything keyed on the entry, such as isosurface ... eds, which needs an entry to fetch the electron density map for.

The change

Write both categories immediately after entering the block, guarded on the identifier being present (mirroring the existing null check):

blockBuilder.enterEntry().enterId().add(pdbId.getId()).leaveColumn().leaveCategory();
blockBuilder.enterStruct().enterEntryId().add(pdbId.getId()).leaveColumn().leaveCategory();

Both are needed, not one: _entry.id is the canonical item and the one Jmol reads, while BioJava's own reader only looks at _struct.entry_id, so omitting that one would leave BioJava still unable to read back what it just wrote.

_struct was not previously written anywhere in this class (only _struct_keywords, a separate category), so there is no duplicate-category concern.

Tests

CifFileSupplierImplTest gains two cases — there was previously nothing asserting that an identifier survives toMMCIF():

  • shouldWriteEntryIdAndSurviveRoundTrip — reads 4hhb.cif.gz, asserts both items are written, reads the text back and asserts getPdbId() still equals 4HHB.
  • shouldNotWriteEntryIdWhenPdbIdIsAbsent — a structure with no identifier gains no empty entry categories.

Verified that the round-trip test fails on unpatched master (_entry.id must be written) and passes with the change; the pre-existing test in that class is unaffected.

Notes

  • Based directly on upstream master (5b29945); independent of any other open PR.
  • Behaviour change is additive — two items appear in written mmCIF files that were absent before, and only when the structure carries an identifier.

AbstractCifFileSupplier put the PDB identifier only in the data_ block
header, so it was lost for every consumer that reads it as a data item:

  - BioJava's own CifStructureConsumerImpl.consumeStruct reads
    _struct.entry_id, so Structure.getPdbId() came back null after a
    write-then-read round trip;
  - Jmol's mmCIF reader takes _M.pdbID from _entry.id, so a structure
    handed over with openStringInline(structure.toMMCIF()) arrived with
    no identifier, which in turn breaks anything keyed on the entry
    (for instance "isosurface ... eds", which needs an entry to fetch
    the electron density map for).

Write both categories when the structure has an identifier. Both are
needed: _entry.id is the canonical item and the one Jmol reads, while
BioJava's own reader only looks at _struct.entry_id.

Adds a round-trip regression test; there was nothing asserting that an
identifier survived toMMCIF().

Fixes biojava#1143
@aalhossary
aalhossary marked this pull request as ready for review August 18, 2026 14:36
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.

mmCIF writer emits no entry identifier: getPdbId() is lost on a write-then-read round trip

1 participant