gt:                 HetatomImpl hetatom = new HetatomImpl();
context: package org.biojava.nbio.structure.io.cif;
  
  import java.time.LocalDate;
  import java.time.ZoneId;
  import java.time.format.DateTimeFormatter;
  import java.time.format.DateTimeFormatterBuilder;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.NoSuchElementException;
  import java.util.Optional;
  import java.util.OptionalInt;
  import java.util.stream.Collectors;
  import java.util.stream.IntStream;
  
  import javax.vecmath.Matrix4d;
  
  import org.biojava.nbio.structure.AminoAcid;
  import org.biojava.nbio.structure.AminoAcidImpl;
  import org.biojava.nbio.structure.Atom;
  import org.biojava.nbio.structure.AtomImpl;
  import org.biojava.nbio.structure.Chain;
  import org.biojava.nbio.structure.ChainImpl;
  import org.biojava.nbio.structure.DBRef;
  import org.biojava.nbio.structure.Element;
  import org.biojava.nbio.structure.EntityInfo;
  import org.biojava.nbio.structure.EntityType;
  import org.biojava.nbio.structure.Group;
  import org.biojava.nbio.structure.GroupType;
  import org.biojava.nbio.structure.HetatomImpl;
  import org.biojava.nbio.structure.NucleotideImpl;
  import org.biojava.nbio.structure.PDBCrystallographicInfo;
  import org.biojava.nbio.structure.PDBHeader;
  import org.biojava.nbio.structure.PdbId;
  import org.biojava.nbio.structure.ResidueNumber;
  import org.biojava.nbio.structure.SeqMisMatch;
  import org.biojava.nbio.structure.SeqMisMatchImpl;
  import org.biojava.nbio.structure.Site;
  import org.biojava.nbio.structure.Structure;
  import org.biojava.nbio.structure.StructureException;
  import org.biojava.nbio.structure.StructureImpl;
  import org.biojava.nbio.structure.StructureTools;
  import org.biojava.nbio.structure.chem.ChemCompGroupFactory;
  import org.biojava.nbio.structure.io.BondMaker;
  import org.biojava.nbio.structure.io.ChargeAdder;
  import org.biojava.nbio.structure.io.EntityFinder;
  import org.biojava.nbio.structure.io.FileParsingParameters;
  import org.biojava.nbio.structure.io.SeqRes2AtomAligner;
  import org.biojava.nbio.structure.quaternary.BioAssemblyInfo;
  import org.biojava.nbio.structure.quaternary.BiologicalAssemblyBuilder;
  import org.biojava.nbio.structure.quaternary.BiologicalAssemblyTransformation;
  import org.biojava.nbio.structure.xtal.CrystalCell;
  import org.biojava.nbio.structure.xtal.SpaceGroup;
  import org.biojava.nbio.structure.xtal.SymoplibParser;
  import org.rcsb.cif.model.FloatColumn;
  import org.rcsb.cif.model.IntColumn;
  import org.rcsb.cif.model.StrColumn;
  import org.rcsb.cif.model.ValueKind;
  import org.rcsb.cif.schema.mm.AtomSite;
  import org.rcsb.cif.schema.mm.AtomSites;
  import org.rcsb.cif.schema.mm.AuditAuthor;
  import org.rcsb.cif.schema.mm.Cell;
  import org.rcsb.cif.schema.mm.ChemComp;
  import org.rcsb.cif.schema.mm.ChemCompBond;
  import org.rcsb.cif.schema.mm.DatabasePDBRemark;
  import org.rcsb.cif.schema.mm.DatabasePDBRev;
  import org.rcsb.cif.schema.mm.DatabasePDBRevRecord;
  import org.rcsb.cif.schema.mm.Em3dReconstruction;
  import org.rcsb.cif.schema.mm.Entity;
  import org.rcsb.cif.schema.mm.EntityPoly;
  import org.rcsb.cif.schema.mm.EntityPolySeq;
  import org.rcsb.cif.schema.mm.EntitySrcGen;
  import org.rcsb.cif.schema.mm.EntitySrcNat;
  import org.rcsb.cif.schema.mm.Exptl;
  import org.rcsb.cif.schema.mm.PdbxAuditRevisionHistory;
  import org.rcsb.cif.schema.mm.PdbxChemCompIdentifier;
  import org.rcsb.cif.schema.mm.PdbxDatabaseStatus;
  import org.rcsb.cif.schema.mm.PdbxEntityBranchDescriptor;
  import org.rcsb.cif.schema.mm.PdbxEntitySrcSyn;
  import org.rcsb.cif.schema.mm.PdbxMolecule;
  import org.rcsb.cif.schema.mm.PdbxMoleculeFeatures;
  import org.rcsb.cif.schema.mm.PdbxNonpolyScheme;
  import org.rcsb.cif.schema.mm.PdbxReferenceEntityLink;
  import org.rcsb.cif.schema.mm.PdbxReferenceEntityList;
  import org.rcsb.cif.schema.mm.PdbxReferenceEntityPolyLink;
  import org.rcsb.cif.schema.mm.PdbxStructAssembly;
  import org.rcsb.cif.schema.mm.PdbxStructAssemblyGen;
  import org.rcsb.cif.schema.mm.PdbxStructModResidue;
  import org.rcsb.cif.schema.mm.PdbxStructOperList;
  import org.rcsb.cif.schema.mm.Refine;
  import org.rcsb.cif.schema.mm.Struct;
  import org.rcsb.cif.schema.mm.StructAsym;
  import org.rcsb.cif.schema.mm.StructConf;
  import org.rcsb.cif.schema.mm.StructConn;
  import org.rcsb.cif.schema.mm.StructConnType;
  import org.rcsb.cif.schema.mm.StructKeywords;
  import org.rcsb.cif.schema.mm.StructNcsOper;
  import org.rcsb.cif.schema.mm.StructRef;
  import org.rcsb.cif.schema.mm.StructRefSeq;
  import org.rcsb.cif.schema.mm.StructRefSeqDif;
  import org.rcsb.cif.schema.mm.StructSheetRange;
  import org.rcsb.cif.schema.mm.StructSite;
  import org.rcsb.cif.schema.mm.StructSiteGen;
  import org.rcsb.cif.schema.mm.Symmetry;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * An implementation of a CifFileConsumer for BioJava. Will process the information provided by a CifFile instance and
   * use it to build up a {@link Structure} object.
   * @author Sebastian Bittrich
   * @since 6.0.0
   */
  public class CifStructureConsumerImpl implements CifStructureConsumer {
      private static final Logger logger = LoggerFactory.getLogger(CifStructureConsumerImpl.class);
      private static final DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder()
              .parseCaseInsensitive()
              .appendPattern("yyyy-MM-dd")
              .toFormatter(Locale.US);
  
      private Structure structure;
      private Chain currentChain;
      private Group currentGroup;
      private List<List<Chain>> allModels;
      private List<Chain> currentModel;
      private PDBHeader pdbHeader;
      private String currentNmrModelNumber;
  	private Em3dReconstruction em3dReconstruction;
      private List<Chain> entityChains;
  
      private Entity entity;
      private EntityPoly entityPoly;
      private EntitySrcGen entitySrcGen;
      private EntitySrcNat entitySrcNat;
      private PdbxEntitySrcSyn entitySrcSyn;
      private List<Chain> seqResChains;
      private PdbxStructAssembly structAssembly;
      private PdbxStructAssemblyGen structAssemblyGen;
      private StructAsym structAsym;
      private StructConn structConn;
      private StructNcsOper structNcsOper;
      private PdbxStructOperList structOpers;
      private StructRef structRef;
      private StructRefSeqDif structRefSeqDif;
      private StructSiteGen structSiteGen;
  
      private Map<String, String> asymId2entityId;
      private Map<String, String> asymId2authorId;
      private Matrix4d parsedScaleMatrix;
  
      private final FileParsingParameters params;
  
      public CifStructureConsumerImpl(FileParsingParameters params) {
          this.params = params;
      }
  
      @Override
      public void prepare() {
          this.structure = new StructureImpl();
          this.pdbHeader = new PDBHeader();
          structure.setPDBHeader(pdbHeader);
  
          this.allModels = new ArrayList<>();
          this.currentModel = new ArrayList<>();
  
          this.seqResChains  = new ArrayList<>();
          this.asymId2entityId = new HashMap<>();
          this.asymId2authorId = new HashMap<>();
  
          this.entityChains = new ArrayList<>();
      }
  
      @Override
      public void consumeAtomSite(AtomSite atomSite) {
          if (params.isHeaderOnly()) {
              return;
          }
  
          StrColumn labelAsymId = atomSite.getLabelAsymId();
          StrColumn authAsymId = atomSite.getAuthAsymId();
  
          StrColumn groupPDB = atomSite.getGroupPDB();
          IntColumn authSeqId = atomSite.getAuthSeqId();
  
          StrColumn labelCompId = atomSite.getLabelCompId();
  
          IntColumn id = atomSite.getId();
          StrColumn labelAtomId = atomSite.getLabelAtomId();
  
          FloatColumn cartnX = atomSite.getCartnX();
          FloatColumn cartnY = atomSite.getCartnY();
          FloatColumn cartnZ = atomSite.getCartnZ();
  
          FloatColumn occupancy = atomSite.getOccupancy();
          FloatColumn bIsoOrEquiv = atomSite.getBIsoOrEquiv();
  
          StrColumn labelAltId = atomSite.getLabelAltId();
          StrColumn typeSymbol = atomSite.getTypeSymbol();
  
          StrColumn pdbxPDBInsCode = atomSite.getPdbxPDBInsCode();
          IntColumn labelSeqId = atomSite.getLabelSeqId();
          IntColumn pdbx_pdb_model_num = atomSite.getPdbxPDBModelNum();
  
          for (int atomIndex = 0; atomIndex < atomSite.getRowCount(); atomIndex++) {
              boolean startOfNewChain = false;
              Character oneLetterCode = StructureTools.get1LetterCodeAmino(labelCompId.get(atomIndex));
  
              boolean isHetAtmInFile = false;
              if (!"ATOM".equals(groupPDB.get(atomIndex))) {
                  if (oneLetterCode != null && oneLetterCode.equals(StructureTools.UNKNOWN_GROUP_LABEL)) {
                      oneLetterCode = null;
                  }
  
                  isHetAtmInFile = true;
              }
  
              String insCodeString = pdbxPDBInsCode.get(atomIndex);
              Character insCode = null;
              if (insCodeString != null && !insCodeString.isEmpty() && !"?".equals(insCodeString)) {
                  insCode = insCodeString.charAt(0);
              }
  
              // non polymer chains (ligands and small molecules) will have a label_seq_id set to '.'
              long seqId = labelSeqId.get(atomIndex);
  
              String nmrModelNumber = pdbx_pdb_model_num.getStringData(atomIndex);
  
              if (currentNmrModelNumber == null) {
                  currentNmrModelNumber = nmrModelNumber;
              }
              if (!currentNmrModelNumber.equals(nmrModelNumber)) {
                  currentNmrModelNumber = nmrModelNumber;
  
                  if (currentChain != null) {
                      currentChain.addGroup(currentGroup);
                      currentGroup.trimToSize();
                  }
  
                  allModels.add(currentModel);
                  currentModel = new ArrayList<>();
                  currentChain = null;
                  currentGroup = null;
              }
  
              String asymId = labelAsymId.get(atomIndex);
              String authId = authAsymId.get(atomIndex);
              if (currentChain == null) {
                  currentChain = new ChainImpl();
                  currentChain.setName(authId);
                  currentChain.setId(asymId);
                  currentModel.add(currentChain);
                  startOfNewChain = true;
              }
  
              if (!asymId.equals(currentChain.getId())) {
                  startOfNewChain = true;
  
                  currentChain.addGroup(currentGroup);
  
                  Optional<Chain> testChain = currentModel.stream()
                          .filter(chain -> chain.getId().equals(asymId))
                          .findFirst();
  
                  if (testChain.isPresent()) {
                      currentChain = testChain.get();
                  } else {
                      currentChain = new ChainImpl();
                      currentChain.setName(authId);
                      currentChain.setId(asymId);
                  }
  
                  if (!currentModel.contains(currentChain)) {
                      currentModel.add(currentChain);
                  }
              }
  
              ResidueNumber residueNumber = new ResidueNumber(authId, authSeqId.get(atomIndex), insCode);
  
              String recordName = groupPDB.get(atomIndex);
              String compId = labelCompId.get(atomIndex);
              if (currentGroup == null) {
                  currentGroup = createGroup(recordName, oneLetterCode, compId, seqId);
                  currentGroup.setResidueNumber(residueNumber);
                  currentGroup.setPDBName(compId);
                  currentGroup.setHetAtomInFile(isHetAtmInFile);
              }
  
              Group altGroup = null;
              String altLocation = labelAltId.get(atomIndex);
  
              if (startOfNewChain) {
                  currentGroup = createGroup(recordName, oneLetterCode, compId, seqId);
                  currentGroup.setResidueNumber(residueNumber);
                  currentGroup.setPDBName(compId);
                  currentGroup.setHetAtomInFile(isHetAtmInFile);
              } else {
                  if (!residueNumber.equals(currentGroup.getResidueNumber())) {
                      currentChain.addGroup(currentGroup);
                      currentGroup.trimToSize();
                      currentGroup = createGroup(recordName, oneLetterCode, compId, seqId);
                      currentGroup.setPDBName(compId);
                      currentGroup.setResidueNumber(residueNumber);
                      currentGroup.setHetAtomInFile(isHetAtmInFile);
                  } else {
                      if (altLocation != null && !altLocation.isEmpty() && !altLocation.equals(".")) {
                          altGroup = getAltLocGroup(recordName, altLocation.charAt(0), oneLetterCode, compId, seqId);
                          if (altGroup.getChain() == null) {
                              altGroup.setChain(currentChain);
                          }
                      }
                  }
              }
  
              if (params.isParseCAOnly()) {
                  if (!labelAtomId.get(atomIndex).equals(StructureTools.CA_ATOM_NAME) && "C".equals(typeSymbol.get(atomIndex))) {
                      continue;
                  }
              }
  
              Atom atom = new AtomImpl();
  
              atom.setPDBserial(id.get(atomIndex));
              atom.setName(labelAtomId.get(atomIndex));
  
              atom.setX(cartnX.get(atomIndex));
              atom.setY(cartnY.get(atomIndex));
              atom.setZ(cartnZ.get(atomIndex));
  
              atom.setOccupancy((float) occupancy.get(atomIndex));
              atom.setTempFactor((float) bIsoOrEquiv.get(atomIndex));
  
              if (altLocation == null || altLocation.isEmpty() || altLocation.equals(".")) {
                  atom.setAltLoc(' ');
              } else {
                  atom.setAltLoc(altLocation.charAt(0));
              }
  
              String ts = typeSymbol.get(atomIndex);
              try {
                  Element element = Element.valueOfIgnoreCase(ts);
                  atom.setElement(element);
              }  catch (IllegalArgumentException e) {
                  logger.info("Element {} was not recognised as a BioJava-known element, the element will be " +
                          "represented as the generic element {}", ts, Element.R.name());
                  atom.setElement(Element.R);
              }
  
              if (altGroup != null) {
                  altGroup.addAtom(atom);
              } else {
                  currentGroup.addAtom(atom);
              }
  
              String atomName = atom.getName();
              if (!currentGroup.hasAtom(atomName)) {
                  if (currentGroup.getPDBName().equals(atom.getGroup().getPDBName())) {
                      if (!StructureTools.hasNonDeuteratedEquiv(atom, currentGroup)) {
                          currentGroup.addAtom(atom);
                      }
                  }
              }
          }
      }
  
      private Group getAltLocGroup(String recordName, Character altLoc, Character oneLetterCode, String threeLetterCode,
                                   long seqId) {
          List<Atom> atoms = currentGroup.getAtoms();
          if (atoms.size() > 0) {
              if (atoms.get(0).getAltLoc().equals(altLoc)) {
                  return currentGroup;
              }
          }
  
          List<Group> altLocs = currentGroup.getAltLocs();
          for (Group altLocGroup : altLocs) {
              atoms = altLocGroup.getAtoms();
              if (atoms.size() > 0) {
                  for (Atom a1 : atoms) {
                      if (a1.getAltLoc().equals(altLoc)) {
                          return altLocGroup;
                      }
                  }
              }
          }
  
          if (threeLetterCode.equals(currentGroup.getPDBName())) {
              if (currentGroup.getAtoms().isEmpty()) {
                  return currentGroup;
              }
  
              Group altLocGroup = (Group) currentGroup.clone();
              altLocGroup.setAtoms(new ArrayList<>());
              altLocGroup.getAltLocs().clear();
              currentGroup.addAltLoc(altLocGroup);
              return altLocGroup;
          }
  
          Group altLocGroup = createGroup(recordName, oneLetterCode, threeLetterCode, seqId);
          altLocGroup.setPDBName(threeLetterCode);
          altLocGroup.setResidueNumber(currentGroup.getResidueNumber());
          currentGroup.addAltLoc(altLocGroup);
          return altLocGroup;
      }
  
      private Group createGroup(String record, Character oneLetterCode, String threeLetterCode, long seqId) {
          Group group = ChemCompGroupFactory.getGroupFromChemCompDictionary(threeLetterCode);
          if (group != null && !group.getChemComp().isEmpty()) {
              if (group instanceof AminoAcidImpl) {
                  AminoAcidImpl aminoAcid = (AminoAcidImpl) group;
                  aminoAcid.setId(seqId);
              } else if (group instanceof NucleotideImpl) {
                  NucleotideImpl nucleotide = (NucleotideImpl) group;
                  nucleotide.setId(seqId);
              } else if (group instanceof HetatomImpl) {
                  HetatomImpl hetatom = (HetatomImpl) group;
                  hetatom.setId(seqId);
              }
              return group;
          }
  
          if ("ATOM".equals(record)) {
              if (StructureTools.isNucleotide(threeLetterCode)) {
                  NucleotideImpl nucleotide = new NucleotideImpl();
                  group = nucleotide;
                  nucleotide.setId(seqId);
              } else if (oneLetterCode == null || oneLetterCode == StructureTools.UNKNOWN_GROUP_LABEL) {
                  HetatomImpl hetatom = new HetatomImpl();
                  group = hetatom;
                  hetatom.setId(seqId);
              } else {
                  AminoAcidImpl aminoAcid = new AminoAcidImpl();
                  group = aminoAcid;
                  aminoAcid.setAminoType(oneLetterCode);
                  aminoAcid.setId(seqId);
              }
          } else {
              if (StructureTools.isNucleotide(threeLetterCode)) {
                  NucleotideImpl nucleotide = new NucleotideImpl();
                  group = nucleotide;
                  nucleotide.setId(seqId);
              } else if (oneLetterCode != null) {
                  AminoAcidImpl aminoAcid = new AminoAcidImpl();
                  group = aminoAcid;
                  aminoAcid.setAminoType(oneLetterCode);
                  aminoAcid.setId(seqId);
              } else {
  