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