001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on 26.04.2004
021 * @author Andreas Prlic
022 *
023 */
024package org.biojava.nbio.structure;
025
026import java.util.ArrayList;
027import java.util.List;
028import java.util.ListIterator;
029
030import org.biojava.nbio.structure.io.FileConvert;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Implementation of a PDB Structure. This class
036 * provides the data contained in a PDB file.
037 * to get structure objects from different sources
038 * see io package.
039 *
040 * @author Andreas Prlic
041 * @author Jules Jacobsen
042 * @since 1.4
043 * @version %I% %G%
044 */
045public class StructureImpl implements Structure {
046
047        private static final long serialVersionUID = -8344837138032851348L;
048
049        private static final Logger logger = LoggerFactory.getLogger(StructureImpl.class);
050
051        private PdbId pdbId ;
052
053        private List<Model> models;
054
055        private List<EntityInfo> entityInfos;
056        private List<DBRef> dbrefs;
057        private List<Bond> ssbonds;
058        private List<Site> sites;
059        private String name ;
060        private StructureIdentifier structureIdentifier;
061
062        private PDBHeader pdbHeader;
063
064        private boolean biologicalAssembly;
065
066        /**
067         *  Constructs a StructureImpl object.
068         */
069        public StructureImpl() {
070                super();
071
072                models         = new ArrayList<>();
073                name           = "";
074                entityInfos      = new ArrayList<>();
075                dbrefs         = new ArrayList<>();
076                pdbHeader      = new PDBHeader();
077                ssbonds        = new ArrayList<>();
078                sites          = new ArrayList<>();
079        }
080
081        /**
082         * Construct a Structure object that only contains a single group
083         *
084         * @param g group object
085         */
086        public StructureImpl(Group g){
087                this();
088
089                Chain c = new ChainImpl();
090                c.addGroup(g);
091
092                addChain(c);
093        }
094
095        /**
096         * Construct a Structure object that contains a particular chain
097         *
098         * @param c chain
099         */
100        public StructureImpl(Chain c){
101                this();
102                addChain(c);
103        }
104
105        /**
106         * Returns an identical copy of this structure .
107         * @return an identical Structure object
108         */
109        @Override
110        public Structure clone() {
111                // Note: structures are also cloned in SubstructureIdentifier.reduce().
112                // Changes might need to be made there as well
113
114                Structure n = new StructureImpl();
115                // go through whole substructure and clone ...
116
117                // copy structure data
118
119                n.setPdbId(getPdbId());
120                n.setName(getName());
121                //TODO the header data is not being deep-copied, that's a minor issue since it is just some static metadata, but we should recheck this if needed - JD 2014-12-11
122                n.setPDBHeader(pdbHeader);
123                n.setDBRefs(this.getDBRefs());
124                n.setSites(getSites());
125
126
127                // go through each chain and clone chain
128                for (int i=0;i<nrModels();i++){
129                        List<Chain> cloned_model = new ArrayList<Chain>();
130
131                        for (int j=0;j<size(i);j++){
132
133                                Chain cloned_chain  = (Chain) getChainByIndex(i,j).clone();
134
135                                // setting the parent: can only be done from the parent
136                                cloned_chain.setStructure(n);
137
138                                cloned_model.add(cloned_chain);
139
140                        }
141                        n.addModel(cloned_model);
142
143                }
144
145                // deep-copying of entityInfofos is tricky: there's cross references also in the Chains
146                // beware: if we copy the entityInfos we would also need to reset the references to entityInfos in the individual chains
147                List<EntityInfo> newEntityInfoList = new ArrayList<>();
148                for (EntityInfo entityInfo : this.entityInfos) {
149                        EntityInfo newEntityInfo = new EntityInfo(entityInfo); // this sets everything but the chains
150                        for (String asymId:entityInfo.getChainIds()) {
151
152                                for (int modelNr=0;modelNr<n.nrModels();modelNr++) {
153                                        Chain newChain = n.getChain(asymId,modelNr);
154                                        if (newChain==null) {
155                                                // this actually happens for structure 1msh, which has no chain B for model 29 (clearly a deposition error)
156                                                logger.warn("Could not find chain asymId "+asymId+" of model "+modelNr+" while cloning entityInfo "+entityInfo.getMolId()+". Something is wrong!");
157                                                continue;
158                                        }
159                                        newChain.setEntityInfo(newEntityInfo);
160                                        newEntityInfo.addChain(newChain);
161                                }
162                        }
163                        newEntityInfoList.add(newEntityInfo);
164                }
165                n.setEntityInfos(newEntityInfoList);
166                // TODO ssbonds are complicated to clone: there are deep references inside Atom objects, how would we do it? - JD 2016-03-03
167
168                return n ;
169        }
170
171        /** {@inheritDoc} */
172        @Override
173        public Group findGroup(String chainName, String pdbResnum, int modelnr)
174                        throws StructureException {
175
176
177                // if structure is xray there will be only one "model".
178                if ( modelnr > models.size())
179                        throw new StructureException(" no model nr " + modelnr +
180                                        " in this structure. (contains "+models.size()+")");
181
182
183                // first we need to gather all groups with the author id chainName: polymers, non-polymers and waters
184                Chain polyChain = getPolyChainByPDB(chainName, modelnr);
185                if(polyChain != null) {
186                        List<Group> groups = new ArrayList<>();
187
188                        groups.addAll(polyChain.getAtomGroups());
189
190
191                        // there can be more than one non-poly chain for a given author id
192                        for (Chain chain: getNonPolyChainsByPDB(chainName, modelnr)) {
193                                groups.addAll(chain.getAtomGroups());
194                        }
195
196                        Chain water = getWaterChainByPDB(chainName, modelnr);
197
198                        if (water!=null)
199                                groups.addAll(water.getAtomGroups());
200
201
202
203                        // now iterate over all groups
204                        // in order to find the amino acid that has this pdbRenum.
205
206                        for (Group g : groups) {
207                                String rnum = g.getResidueNumber().toString();
208                                //System.out.println(g + " >" + rnum + "< >" + pdbResnum + "<");
209                                // we only mutate amino acids
210                                // and ignore hetatoms and nucleotides in this case
211                                if (rnum.equals(pdbResnum)) {
212                                        return g;
213                                }
214                        }
215                }
216                throw new StructureException("could not find group " + pdbResnum +
217                                " in chain " + chainName);
218        }
219
220
221        /** {@inheritDoc} */
222        @Override
223        public Group findGroup(String chainName, String pdbResnum) throws StructureException
224        {
225                return findGroup(chainName, pdbResnum, 0);
226
227        }
228
229
230        /** {@inheritDoc} */
231        @Override
232        public void   setName(String nam) { name = nam; }
233
234        /** {@inheritDoc} */
235        @Override
236        public String getName()           { return name;  }
237
238        /**
239         * @return The StructureIdentifier used to create this structure
240         */
241        @Override
242        public StructureIdentifier getStructureIdentifier() {
243                return structureIdentifier;
244        }
245
246        /**
247         * @param structureIdentifier the structureIdentifier corresponding to this structure
248         */
249        @Override
250        public void setStructureIdentifier(StructureIdentifier structureIdentifier) {
251                this.structureIdentifier = structureIdentifier;
252        }
253
254        /** {@inheritDoc} */
255        @Override
256        public void addChain(Chain chain) {
257                int modelnr = 0 ;
258                addChain(chain,modelnr);
259        }
260
261        /** {@inheritDoc} */
262        @Override
263        public void addChain(Chain chain, int modelnr) {
264                // if model has not been initialized, init it!
265                chain.setStructure(this);
266                if (models.isEmpty()) {
267                        Model model = new Model();
268                        List<Chain> modelChains = new ArrayList<>() ;
269                        modelChains.add(chain);
270                        model.setChains(modelChains);
271                        models.add(model);
272
273                } else {
274                        Model model = models.get(modelnr);
275                        model.addChain(chain);
276                }
277
278
279
280        }
281
282
283
284        /** {@inheritDoc} */
285        @Override
286        public Chain getChainByIndex(int number) {
287
288                int modelnr = 0 ;
289
290                return getChainByIndex(modelnr,number);
291        }
292
293
294        /** {@inheritDoc} */
295        @Override
296        public Chain getChainByIndex(int modelnr,int number) {
297
298                Model model = models.get(modelnr);
299
300                return model.getChains().get(number);
301        }
302
303
304
305        /** {@inheritDoc} */
306        @Override
307        public void addModel(List<Chain> modelChains){
308                for (Chain c: modelChains){
309                        c.setStructure(this);
310                }
311                Model model = new Model();
312                model.setChains(modelChains);
313                models.add(model);
314        }
315
316
317        /** {@inheritDoc} */
318        @Override
319        public void setChains(List<Chain> chains){
320
321                setModel(0,chains);
322        }
323
324
325
326        /** {@inheritDoc} */
327        @Override
328        public void setModel(int position, List<Chain> modelChains){
329                if (modelChains == null)
330                        throw new IllegalArgumentException("trying to set model to null!");
331
332                for (Chain c: modelChains)
333                        c.setStructure(this);
334
335                //System.out.println("model size:" + models.size());
336
337
338                Model model = new Model();
339                model.setChains(modelChains);
340
341                if (models.isEmpty()){
342                        models.add(model);
343                } else {
344                        models.set(position, model);
345                }
346        }
347
348        /** String representation.
349         *
350         */
351        @Override
352        public String toString(){
353                String newline = System.getProperty("line.separator");
354                StringBuilder str = new StringBuilder();
355                str.append("structure ");
356                str.append(name);
357                str.append(" ");
358                str.append(pdbId);
359                str.append(" ");
360
361                if ( nrModels()>1 ){
362                        str.append( " models: ");
363                        str.append(nrModels());
364                        str.append(newline) ;
365                }
366
367                str.append(pdbHeader);
368                str.append(newline) ;
369
370                for (int i=0;i<nrModels();i++){
371                        if ( nrModels()>1 ) {
372                                str.append(" model[");
373                                str.append(i);
374                                str.append("]:");
375                                str.append(newline);
376                        }
377                        str.append(" chains:");
378                        str.append(newline);
379
380                        for (int j=0;j<size(i);j++){
381
382                                Chain cha = getChainByIndex(i,j);
383                                List<Group> agr = cha.getAtomGroups(GroupType.AMINOACID);
384                                List<Group> hgr = cha.getAtomGroups(GroupType.HETATM);
385                                List<Group> ngr = cha.getAtomGroups(GroupType.NUCLEOTIDE);
386
387
388
389
390                                str.append("chain ")
391                                                .append(j).append(": asymId:")
392                                                .append(cha.getId())
393                                                .append(" authId:")
394                                                .append(cha.getName()).append(" ");
395
396
397                                if ( cha.getEntityInfo() != null){
398                                        EntityInfo comp = cha.getEntityInfo();
399                                        String molName = comp.getDescription();
400                                        if ( molName != null){
401                                                str.append(molName);
402                                        }
403                                        String type =  comp.getType().toString();
404                                        str.append(" (")
405                                                        .append(type)
406                                                        .append(")");
407                                }
408
409
410                                str.append(newline);
411                                str.append(" length SEQRES: ").append(cha.getSeqResLength());
412                                str.append(" length ATOM: ").append(cha.getAtomLength());
413                                str.append(" aminos: ").append(agr.size());
414                                str.append(" hetatms: ").append(hgr.size());
415                                str.append(" nucleotides: ").append(ngr.size()).append(newline);
416                        }
417
418                }
419                str.append("DBRefs: ").append(dbrefs.size()).append(newline);
420                for (DBRef dbref: dbrefs){
421                        str.append(dbref.toPDB()).append(newline);
422                }
423                str.append("Molecules: ").append(newline);
424                for (EntityInfo mol : entityInfos) {
425                        str.append(mol).append(newline);
426                }
427
428
429                return str.toString() ;
430        }
431
432        @Override
433        public int size() {
434                int modelnr = 0 ;
435
436                if (!models.isEmpty()) {
437                        return models.get(modelnr).getPolyChains().size();
438                }
439                else {
440                        return 0 ;
441                }
442
443        }
444
445        /** return number of chains  of model.
446         *
447         */
448        @Override
449        public int size(int modelnr) { return models.get(modelnr).size(); }
450
451        // some NMR stuff :
452
453        /** return number of models. */
454        @Override
455        public int nrModels() {
456                return models.size() ;
457        }
458
459        /**
460         * Whether this Structure is a crystallographic structure or not.
461         * It will first check the experimental technique and if not present it will try
462         * to guess from the presence of a space group and sensible cell parameters
463         *
464         * @return true if crystallographic, false otherwise
465         */
466        @Override
467        public boolean isCrystallographic() {
468                if (pdbHeader.getExperimentalTechniques()!=null) {
469                        return ExperimentalTechnique.isCrystallographic(pdbHeader.getExperimentalTechniques());
470                } else {
471                        // no experimental technique known, we try to guess...
472                        if (pdbHeader.getCrystallographicInfo().getSpaceGroup()!=null) {
473                                // space group defined but no crystal cell: incomplete info, return false
474                                return  pdbHeader.getCrystallographicInfo().getCrystalCell() != null &&
475                                                pdbHeader.getCrystallographicInfo().getCrystalCell().isCellReasonable();
476                        }
477                }
478                return false;
479        }
480
481        /**
482         * Whether this Structure is a NMR structure or not.
483         * It will first check the experimental technique and if not present it will try
484         * to guess from the presence of more than 1 model and from b-factors being 0 in first chain of first model
485         * @return true if NMR, false otherwise
486         */
487        @Override
488        public boolean isNmr() {
489
490                // old implementation was:
491                //return nmrflag;
492
493                if (pdbHeader.getExperimentalTechniques()!=null) {
494                        return ExperimentalTechnique.isNmr(pdbHeader.getExperimentalTechniques());
495                } else {
496                        // no experimental technique known, we try to guess...
497                        if (nrModels()>1) {
498                                if (pdbHeader.getCrystallographicInfo().getSpaceGroup()!=null) {
499                                        // multimodel, sg defined, but missing cell: must be NMR
500                                        if (pdbHeader.getCrystallographicInfo().getCrystalCell()==null)
501                                                return true;
502                                        // multi-model, sg defined and cell unreasonable: must be NMR
503                                        if (!pdbHeader.getCrystallographicInfo().getCrystalCell().isCellReasonable())
504                                                return true;
505                                } else {
506                                        // multi-model and missing space group: must be NMR
507                                        return true;
508                                }
509                        }
510                }
511                return false;
512        }
513
514        /** {@inheritDoc} */
515        @Override
516        public List<Chain> getChains(int modelIdx){
517                return getModel(modelIdx);
518        }
519
520        /** {@inheritDoc} */
521        @Override
522        public List<Chain> getChains(){
523                if (models.size()==0) {
524                        return new ArrayList<>(0);
525                }
526                return getChains(0);
527
528        }
529
530        @Override
531        public List<Chain> getPolyChains() {
532                if (models.size()==0) {
533                        return new ArrayList<>(0);
534                }
535                return getPolyChains(0);
536        }
537
538        @Override
539        public List<Chain> getPolyChains(int modelIdx) {
540                return models.get(modelIdx).getPolyChains();
541        }
542
543        @Override
544        public List<Chain> getNonPolyChains() {
545                if (models.size()==0) {
546                        return new ArrayList<>(0);
547                }
548                return  getNonPolyChains(0);
549        }
550
551        @Override
552        public List<Chain> getNonPolyChains(int modelIdx) {
553                return models.get(modelIdx).getNonPolyChains();
554        }
555
556        @Override
557        public List<Chain> getWaterChains() {
558                if (models.size()==0) {
559                        return new ArrayList<>(0);
560                }
561                return getWaterChains(0);
562        }
563
564        @Override
565        public List<Chain> getWaterChains(int modelIdx) {
566                return models.get(modelIdx).getWaterChains();
567        }
568
569
570
571        /** {@inheritDoc} */
572        @Override
573        public void setChains(int modelnr, List<Chain> chains){
574                for (Chain c: chains){
575                        c.setStructure(this);
576                }
577                if (models.size()>modelnr) {
578                        models.remove(modelnr);
579                }
580
581                Model model = new Model();
582                model.setChains(chains);
583                models.add(modelnr, model);
584
585        }
586
587        /** Retrieve all Chains belonging to a model .
588         *
589         * @param modelnr  an int
590         * @return a List object
591         */
592        @Override
593        public List<Chain> getModel(int modelnr) {
594
595                return models.get(modelnr).getChains();
596        }
597
598        /** {@inheritDoc} */
599        @Override
600        public Chain getChain(String asymId, int modelnr) {
601
602                List<Chain> chains = getChains(modelnr);
603                for (Chain c : chains) {
604                        if (c.getId().equals(asymId)) {
605                                return c;
606                        }
607                }
608                return null;
609
610        }
611
612        /** {@inheritDoc} */
613        @Override
614        public Chain getChain(String asymId) {
615
616                return getChain(asymId,0);
617
618        }
619
620        @Override
621        public Chain getPolyChain(String asymId) {
622                return getPolyChain(asymId, 0);
623
624        }
625
626        @Override
627        public Chain getPolyChain(String asymId, int modelIdx) {
628                Model model = models.get(modelIdx);
629                if (model==null) {
630                        return null;
631                }
632                List<Chain> polyChains = model.getPolyChains();
633                for (Chain c : polyChains){
634                        if (c.getId().equals(asymId))
635                                return c;
636                }
637                return null;
638        }
639
640
641        @Override
642        public Chain getNonPolyChain(String asymId) {
643                return getNonPolyChain(asymId, 0);
644        }
645
646        @Override
647        public Chain getNonPolyChain(String asymId, int modelIdx) {
648                Model model = models.get(modelIdx);
649                if (model==null) {
650                        return null;
651                }
652
653                List<Chain> nonpolyChains = model.getNonPolyChains();
654                for (Chain c : nonpolyChains){
655                        if (c.getId().equals(asymId))
656                                return c;
657                }
658
659                return null;
660        }
661
662        @Override
663        public Chain getPolyChainByPDB(String authId) {
664                return getPolyChainByPDB(authId, 0);
665        }
666
667        @Override
668        public Chain getPolyChainByPDB(String authId, int modelIdx) {
669                Model model = models.get(modelIdx);
670                if (model==null) {
671                        return null;
672                }
673
674                List<Chain> polyChains = model.getPolyChains();
675                for (Chain c : polyChains){
676                        if (c.getName().equals(authId))
677                                return c;
678                }
679
680                return null;
681        }
682
683        @Override
684        public List<Chain> getNonPolyChainsByPDB(String authId) {
685                return getNonPolyChainsByPDB(authId, 0);
686        }
687
688        @Override
689        public List<Chain> getNonPolyChainsByPDB(String authId, int modelIdx) {
690                List<Chain> chains = new ArrayList<>();
691                Model model = models.get(modelIdx);
692                if (model==null) {
693                        return chains;
694                }
695
696
697                List<Chain> nonpolyChains = model.getNonPolyChains();
698                for (Chain c : nonpolyChains){
699                        if (c.getName().equals(authId))
700                                chains.add(c);
701                }
702
703                return chains;
704        }
705
706        @Override
707        public Chain getWaterChain(String asymId) {
708                return getWaterChain(asymId, 0);
709        }
710
711
712        @Override
713        public Chain getWaterChain(String asymId, int modelIdx) {
714                Model model = models.get(modelIdx);
715                if (model==null) {
716                        return null;
717                }
718                List<Chain> waterChains = model.getWaterChains();
719                for (Chain c : waterChains){
720                        if (c.getId().equals(asymId))
721                                return c;
722                }
723                return null;
724        }
725
726
727        @Override
728        public Chain getWaterChainByPDB(String authId) {
729                return getWaterChainByPDB(authId, 0);
730        }
731
732
733        @Override
734        public Chain getWaterChainByPDB(String authId, int modelIdx) {
735                Model model = models.get(modelIdx);
736                if (model==null) {
737                        return null;
738                }
739                List<Chain> waterChains = model.getWaterChains();
740                for (Chain c : waterChains){
741                        if (c.getName().equals(authId))
742                                return c;
743                }
744
745                return null;
746        }
747
748
749
750        /** {@inheritDoc} */
751        @Override
752        public String toPDB() {
753                FileConvert f = new FileConvert(this) ;
754                return f.toPDB();
755        }
756
757        /** {@inheritDoc} */
758        @Override
759        public String toMMCIF() {
760                FileConvert f = new FileConvert(this);
761                return f.toMMCIF();
762        }
763
764        /** {@inheritDoc} */
765        @Override
766        public boolean hasChain(String authId) {
767                int modelnr = 0;
768
769                List<Chain> chains = getChains(modelnr);
770                for (Chain c : chains) {
771                        // we check here with equals because we might want to distinguish between upper and lower case chains!
772                        if (c.getId().equals(authId)) {
773                                return true;
774                        }
775                }
776                return false;
777        }
778
779        /** {@inheritDoc} */
780        @Override
781        public boolean hasNonPolyChain(String asymId){
782                int modelnr = 0;
783
784                List<Chain> chains = models.get(modelnr).getNonPolyChains();
785                for (Chain c : chains) {
786                        // we check here with equals because we might want to distinguish between upper and lower case chains!
787                        if (c.getId().equals(asymId)) {
788                                return true;
789                        }
790                }
791                return false;
792        }
793
794        /** {@inheritDoc} */
795        @Override
796        public boolean hasPdbChain(String authId) {
797                int modelnr = 0;
798
799                List<Chain> chains = getChains(modelnr);
800                for (Chain c : chains) {
801                        // we check here with equals because we might want to distinguish between upper and lower case chains!
802                        if (c.getName().equals(authId)) {
803                                return true;
804                        }
805                }
806                return false;
807        }
808
809        /** {@inheritDoc} */
810        @Override
811        public void setEntityInfos(List<EntityInfo> molList){
812                this.entityInfos = molList;
813        }
814
815        /** {@inheritDoc} */
816        @Override
817        public void addEntityInfo(EntityInfo entityInfo) {
818                this.entityInfos.add(entityInfo);
819        }
820
821        /** {@inheritDoc} */
822        @Override
823        public List<EntityInfo> getEntityInfos() {
824                return entityInfos;
825        }
826
827        /** {@inheritDoc} */
828        @Override
829        public EntityInfo getEntityById(int entityId) {
830                for (EntityInfo mol : this.entityInfos){
831                        if (mol.getMolId()==entityId){
832                                return mol;
833                        }
834                }
835                return null;
836        }
837
838
839        /** {@inheritDoc} */
840        @Override
841        public List<DBRef> getDBRefs() {
842                return dbrefs;
843        }
844
845
846        /** {@inheritDoc} */
847        @Override
848        public void setDBRefs(List<DBRef> dbrefs) {
849                if ( dbrefs == null)
850                        throw new IllegalArgumentException("trying to set dbrefs to null!");
851
852                for( DBRef ref : dbrefs){
853                        ref.setParent(this);
854                }
855                this.dbrefs = dbrefs;
856        }
857
858
859        /** {@inheritDoc} */
860        @Override
861        public PDBHeader getPDBHeader() {
862                return pdbHeader;
863        }
864
865        /** {@inheritDoc} */
866        @Override
867        public void setPDBHeader(PDBHeader pdbHeader){
868                this.pdbHeader = pdbHeader;
869        }
870
871        /** {@inheritDoc} */
872        @Override
873        public List<Bond> getSSBonds(){
874                return ssbonds;
875
876        }
877
878        /** {@inheritDoc} */
879        @Override
880        public void setSSBonds(List<Bond> ssbonds){
881                this.ssbonds = ssbonds;
882        }
883
884        /**
885         * Adds a single disulfide Bond to this structure
886         *
887         * @param ssbond the SSBond.
888         */
889        @Override
890        public void addSSBond(Bond ssbond){
891                ssbonds.add(ssbond);
892        }
893
894        /**
895         * Return whether or not the entry has an associated journal article
896         * or publication. The JRNL section is not mandatory and thus may not be
897         * present.
898         * @return flag if a JournalArticle could be found.
899         */
900        @Override
901        public boolean hasJournalArticle() {
902                return this.pdbHeader.hasJournalArticle();
903        }
904
905        /**
906         * get the associated publication as defined by the JRNL records in a PDB
907         * file.
908         * @return a JournalArticle
909         */
910        @Override
911        public JournalArticle getJournalArticle() {
912                return this.pdbHeader.getJournalArticle();
913        }
914
915        /**
916         * set the associated publication as defined by the JRNL records in a PDB
917         * file.
918         * @param journalArticle the article
919         */
920        @Override
921        public void setJournalArticle(JournalArticle journalArticle) {
922                this.pdbHeader.setJournalArticle(journalArticle);
923        }
924
925        /**
926         * @return the sites contained in this structure
927         */
928
929        @Override
930        public List<Site> getSites() {
931                return sites;
932        }
933
934        /**
935         * @param sites the sites to set in the structure
936         */
937
938        @Override
939        public void setSites(List<Site> sites) {
940                this.sites = sites;
941        }
942
943        /** Caution: we should probably remove this to avoid confusion. Currently this is always an empty list!
944         *
945         * @return a list of Groups listed in the HET records - this will not
946         * include any waters.
947         */
948
949        /**
950         * Sets a flag to indicate if this structure is a biological assembly
951         * @param biologicalAssembly true if biological assembly, otherwise false
952         * @since 3.2
953         */
954        @Override
955        public void setBiologicalAssembly(boolean biologicalAssembly) {
956                this.biologicalAssembly = biologicalAssembly;
957        }
958
959        /**
960         * Gets flag that indicates if this structure is a biological assembly
961         * @return the sites contained in this structure
962         * @since 3.2
963         */
964        @Override
965        public boolean isBiologicalAssembly() {
966                return biologicalAssembly;
967        }
968
969        /**
970         * Sets crystallographic information for this structure
971         * @param crystallographicInfo crystallographic information
972         * @since 3.2
973         */
974
975        @Override
976        public void setCrystallographicInfo(PDBCrystallographicInfo crystallographicInfo) {
977                this.pdbHeader.setCrystallographicInfo(crystallographicInfo);
978        }
979
980        /**
981         * Gets crystallographic information for this structure
982         * @return PDBCrystallographicInfo crystallographic information
983         * @since 3.2
984         */
985        @Override
986        public PDBCrystallographicInfo getCrystallographicInfo() {
987                return pdbHeader.getCrystallographicInfo();
988        }
989
990        /** {@inheritDoc} */
991        @Override
992        public String getIdentifier() {
993                //1. StructureIdentifier
994                if(getStructureIdentifier() != null) {
995                        return getStructureIdentifier().getIdentifier();
996                }
997                //2. Name
998                if(getName() != null) {
999                        return getName();
1000                }
1001                //3. PDBCode + ranges
1002                return toCanonical().getIdentifier();
1003        }
1004
1005        /** 
1006         * {@inheritDoc} 
1007         * @deprecated use {@link #getPdbId()} to get a {@link PdbId} object or getPdbId().getId() to get a {@link String}
1008         */
1009        @Deprecated
1010        @Override
1011        public String  getPDBCode () {
1012                if(pdbId == null)
1013                        return null;
1014                return this.pdbId.getId() ;
1015        }
1016        
1017        /** {@inheritDoc} 
1018         * @deprecated use {@link #setPDBCode(PdbId)}
1019         * */
1020        @Deprecated
1021        @Override
1022        public void setPDBCode(String pdb_id){
1023                if(pdb_id == null) {
1024                        this.pdbId = null;
1025                }else {
1026                        pdbId = new PdbId(pdb_id);
1027                }
1028        }
1029        
1030
1031
1032        /** {@inheritDoc} 
1033         * @since 6.0.0
1034         * */
1035        public PdbId getPdbId() {
1036                return this.pdbId;
1037        }
1038        
1039        /** {@inheritDoc}
1040         * @since 6.0.0
1041         *  */
1042        public void setPdbId(PdbId pdbId) {
1043                this.pdbId = pdbId;
1044        }
1045
1046        @Override
1047        public void resetModels() {
1048                models = new ArrayList<>();
1049        }
1050
1051        /**
1052         * Creates a SubstructureIdentifier based on the residues in this Structure.
1053         *
1054         * Only the first and last residues of each chain are considered, so chains
1055         * with gaps
1056         * @return A {@link SubstructureIdentifier} with residue ranges constructed from each chain
1057         */
1058        private SubstructureIdentifier toCanonical() {
1059                StructureIdentifier real = getStructureIdentifier();
1060                if(real != null) {
1061                        try {
1062                                return real.toCanonical();
1063                        } catch (StructureException e) {
1064                                // generate fake one if needed
1065                        }
1066                }
1067
1068                // No identifier set, so generate based on residues present in the structure
1069                List<ResidueRange> range = new ArrayList<>();
1070                for (Chain chain : getChains()) {
1071                        List<Group> groups = chain.getAtomGroups();
1072                        ListIterator<Group> groupsIt = groups.listIterator();
1073                        if(!groupsIt.hasNext()) {
1074                                continue; // no groups in chain
1075                        }
1076                        Group g = groupsIt.next();
1077                        ResidueNumber first = g.getResidueNumber();
1078
1079                        //TODO Detect missing intermediate residues -sbliven, 2015-01-28
1080                        //Already better than previous whole-chain representation
1081
1082                        // get last residue
1083                        while(groupsIt.hasNext()) {
1084                                g = groupsIt.next();
1085                        }
1086                        ResidueNumber last = g.getResidueNumber();
1087
1088                        range.add(new ResidueRange(chain.getName(),first,last));
1089                }
1090                return new SubstructureIdentifier(getPdbId(),range);
1091        }
1092
1093}