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