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 */ 021package org.biojava.nbio.structure; 022 023import org.biojava.nbio.structure.io.mmcif.model.DatabasePdbrevRecord; 024import org.biojava.nbio.structure.quaternary.BioAssemblyInfo; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import java.lang.reflect.InvocationTargetException; 029import java.lang.reflect.Method; 030import java.text.DateFormat; 031import java.text.DecimalFormat; 032import java.text.NumberFormat; 033import java.text.SimpleDateFormat; 034import java.util.*; 035 036 037/** 038 * A class that contains PDB Header information. 039 * 040 * @author Andreas Prlic 041 * @since 1.6 042 * 043 */ 044public class PDBHeader implements PDBRecord { 045 046 private static final long serialVersionUID = -5834326174085429508L; 047 048 private static final Logger logger = LoggerFactory.getLogger(PDBHeader.class); 049 050 private String title; 051 private String description; 052 private String idCode; 053 private String classification; 054 055 private Date depDate; 056 private Date relDate; 057 private Date modDate; 058 059 private Set<ExperimentalTechnique> techniques; 060 private PDBCrystallographicInfo crystallographicInfo; 061 062 private float resolution; 063 private float rFree; 064 private float rWork; 065 066 private JournalArticle journalArticle; 067 private String authors; 068 069 public static final float DEFAULT_RESOLUTION = 99; 070 public static final float DEFAULT_RFREE = 1; // worst possible rfree is the default 071 072 073 private Long id; 074 public static final String newline = System.getProperty("line.separator"); 075 076 private DateFormat dateFormat; 077 078 private Map<Integer,BioAssemblyInfo> bioAssemblies ; 079 080 List<DatabasePdbrevRecord> revisionRecords; 081 082 public PDBHeader(){ 083 084 depDate = new Date(0); 085 modDate = new Date(0); 086 relDate = new Date(0); 087 dateFormat = new SimpleDateFormat("dd-MMM-yy",Locale.US); 088 089 resolution = DEFAULT_RESOLUTION; 090 rFree = DEFAULT_RFREE; 091 rWork = DEFAULT_RFREE; 092 093 bioAssemblies = new LinkedHashMap<Integer, BioAssemblyInfo>(); 094 crystallographicInfo = new PDBCrystallographicInfo(); 095 096 } 097 098 /** String representation 099 * 100 */ 101 @Override 102 public String toString(){ 103 StringBuilder buf = new StringBuilder(); 104 105 try { 106 107 108 Class<?> c = Class.forName(PDBHeader.class.getName()); 109 Method[] methods = c.getMethods(); 110 111 for (Method m : methods) { 112 String name = m.getName(); 113 114 if (name.substring(0, 3).equals("get")) { 115 if (name.equals("getClass")) { 116 continue; 117 } 118 Object o = m.invoke(this); 119 if (o != null) { 120 buf.append(name.substring(3, name.length())); 121 buf.append(": ").append(o).append(" "); 122 } 123 } 124 } 125 } catch (ClassNotFoundException e) { 126 logger.error("Exception caught while creating toString ",e); 127 } catch (InvocationTargetException e) { 128 logger.error("Exception caught while creating toString ",e); 129 } catch (IllegalAccessException e) { 130 logger.error("Exception caught while creating toString ",e); 131 } 132 133 return buf.toString(); 134 } 135 136 /** Return a PDB representation of the PDB Header 137 * 138 * @return a PDB file style display 139 */ 140 @Override 141 public String toPDB(){ 142 StringBuffer buf = new StringBuffer(); 143 toPDB(buf); 144 return buf.toString(); 145 } 146 147 /** Appends a PDB representation of the PDB header to the provided StringBuffer 148 * 149 * @param buf 150 */ 151 @Override 152 public void toPDB(StringBuffer buf){ 153 // 1 2 3 4 5 6 7 154 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 155 //HEADER COMPLEX (SERINE PROTEASE/INHIBITORS) 06-FEB-98 1A4W 156 //TITLE CRYSTAL STRUCTURES OF THROMBIN WITH THIAZOLE-CONTAINING 157 //TITLE 2 INHIBITORS: PROBES OF THE S1' BINDING SITE 158 159 printHeader(buf); 160 printTitle(buf); 161 printExpdata(buf); 162 printAuthors(buf); 163 printResolution(buf); 164 165 } 166 167 private void printResolution(StringBuffer buf){ 168 169 if (getResolution() == DEFAULT_RESOLUTION){ 170 return; 171 } 172 173 DecimalFormat d2 = (DecimalFormat)NumberFormat.getInstance(java.util.Locale.UK); 174 d2.setMaximumIntegerDigits(2); 175 d2.setMinimumFractionDigits(2); 176 d2.setMaximumFractionDigits(2); 177 178 buf.append("REMARK 2 RESOLUTION. "); 179 String x = d2.format(resolution); 180 buf.append(x); 181 buf.append(" ANGSTROMS."); 182 fillLine(buf,34+x.length()); 183 184 buf.append(newline); 185 } 186 187 private void printExpdata(StringBuffer buf){ 188 Set<ExperimentalTechnique> exp = getExperimentalTechniques(); 189 if ( exp == null ) 190 return; 191 192 193 buf.append("EXPDTA "); 194 195 int length = 0; 196 int i = 0; 197 for (ExperimentalTechnique et:exp) { 198 if (i>0) { 199 buf.append("; "); 200 length+=2; 201 } 202 buf.append(et.getName()); 203 length+=et.getName().length(); 204 i++; 205 } 206 207 // fill up the white space to the right column 208 int l = length + 10; 209 fillLine(buf,l); 210 211 buf.append(newline); 212 213 } 214 215 private void printAuthors(StringBuffer buf){ 216 String authors = getAuthors(); 217 if ( authors == null) 218 return; 219 if ( authors.equals("")){ 220 return; 221 } 222 223 printMultiLine(buf, "AUTHOR ", authors,','); 224 225 } 226 227 private void printMultiLine(StringBuffer buf, String lineStart, String data, char breakChar){ 228 if ( lineStart.length() != 9) 229 logger.info("lineStart != 9, there will be problems :" + lineStart); 230 231 if ( data.length() < 58) { 232 buf.append(lineStart); 233 buf.append(" "); 234 buf.append(data); 235 buf.append(newline); 236 return; 237 } 238 String thisLine = ""; 239 int count = 1; 240 while (data.length() > 57) { 241 // find first whitespace from left 242 // there are 10 chars to the left, so the cutoff position is 56 243 boolean charFound = false; 244 for ( int i =57;i>-1;i--){ 245 char c = data.charAt(i); 246 if (c == breakChar){ 247 // found the whitespace 248 249 thisLine = data.substring(0,i+1); 250 251 // prevent endless loop 252 if (i == 0 ) 253 i++; 254 data = data.substring(i); 255 charFound = true; 256 break; 257 } 258 } 259 // for emergencies... prevents an endless loop 260 if ( ! charFound){ 261 thisLine = data.substring(0,58); 262 data = data.substring(57); 263 } 264 if ( ( breakChar == ',' ) && ( data.charAt(0)== ',')) { 265 data = data.substring(1); 266 } 267 268 //TODO: check structures that have more than 10 lines... 269 // start printing.. 270 271 buf.append(lineStart); 272 if ( count > 1) { 273 buf.append(count); 274 if ( breakChar != ' ' ) 275 buf.append(" "); 276 } 277 else 278 buf.append(" "); 279 buf.append(thisLine); 280 281 // fill up the white space to the right column 282 int l = thisLine.length()+ 10; 283 while (l < 67){ 284 l++; 285 buf.append(" "); 286 } 287 288 buf.append(newline); 289 count++; 290 291 } 292 293 // last line... 294 if (!data.trim().isEmpty()){ 295 buf.append(lineStart); 296 buf.append(count); 297 int filledLeft = 10; 298 if ( breakChar != ' ' ) { 299 buf.append(" "); 300 filledLeft++; 301 } 302 buf.append(data); 303 // fill up the white space to the right column 304 int l = data.length()+ filledLeft; 305 fillLine(buf,l); 306 buf.append(newline); 307 } 308 309 } 310 311 private void fillLine(StringBuffer buf, int currentPos){ 312 int l = currentPos; 313 while (l < 67){ 314 l++; 315 buf.append(" "); 316 } 317 } 318 319 private void printHeader(StringBuffer buf){ 320 321 String classification = getClassification(); 322 323 if (classification == null || classification.isEmpty()) return; 324 325 // we can;t display this line since the classification is not there... 326 327 buf.append("HEADER "); 328 buf.append(classification); 329 buf.append(" "); 330 331 // fill up the white space to the right column 332 int l = classification.length() + 10 ; 333 while (l < 49){ 334 l++; 335 buf.append(" "); 336 } 337 338 Date d = getDepDate(); 339 if ( d != null){ 340 // provide correct display of Dep date... 341 buf.append(dateFormat.format(d)); 342 } else { 343 buf.append(" "); 344 } 345 buf.append(" "); 346 347 String id = getIdCode(); 348 if ( id != null){ 349 buf.append(getIdCode()); 350 buf.append(" "); 351 } 352 else 353 buf.append(" "); 354 buf.append(newline); 355 356 357 } 358 359 private void printTitle(StringBuffer buf) { 360 // 1 2 3 4 5 6 7 361 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 362 363 //HEADER COMPLEX (SERINE PROTEASE/INHIBITORS) 06-FEB-98 1A4W 364 //TITLE CRYSTAL STRUCTURES OF THROMBIN WITH THIAZOLE-CONTAINING 365 //TITLE 2 INHIBITORS: PROBES OF THE S1' BINDING SITE 366 367 String title = getTitle(); 368 369 if ( (title == null) || (title.trim().isEmpty()) ) 370 return; 371 372 printMultiLine(buf, "TITLE ", title,' '); 373 374 } 375 376 /** Get the ID used by Hibernate. 377 * 378 * @return the ID used by Hibernate 379 * @see #setId(Long) 380 */ 381 public Long getId() { 382 return id; 383 } 384 385 /** Set the ID used by Hibernate. 386 * 387 * @param id the id assigned by Hibernate 388 * @see #getId() 389 * 390 */ 391 392 @SuppressWarnings("unused") 393 private void setId(Long id) { 394 this.id = id; 395 } 396 397 /** Compare two PDBHeader objects 398 * 399 * @param other a PDBHeader object to compare this one to. 400 * @return true if they are equal or false if they are not. 401 */ 402 public boolean equals(PDBHeader other){ 403 try { 404 405 Class<?> c = Class.forName(PDBHeader.class.getName()); 406 Method[] methods = c.getMethods(); 407 408 for (Method m : methods) { 409 String name = m.getName(); 410 411 if (name.substring(0, 3).equals("get")) { 412 if (name.equals("getClass")) { 413 continue; 414 } 415 Object a = m.invoke(this); 416 Object b = m.invoke(other); 417 if (a == null) { 418 if (b == null) { 419 continue; 420 } else { 421 logger.warn(name + " a is null, where other is " + b); 422 return false; 423 } 424 } 425 if (b == null) { 426 logger.warn(name + " other is null, where a is " + a); 427 return false; 428 } 429 if (!(a.equals(b))) { 430 logger.warn("mismatch with " + name + " >" + a + "< >" + b + "<"); 431 return false; 432 } 433 } 434 } 435 } catch (ClassNotFoundException e) { 436 logger.error("Exception caught while comparing PDBHeader objects ",e); 437 return false; 438 } catch (InvocationTargetException e) { 439 logger.error("Exception caught while comparing PDBHeader objects ",e); 440 return false; 441 } catch (IllegalAccessException e) { 442 logger.error("Exception caught while comparing PDBHeader objects ",e); 443 return false; 444 } 445 return true; 446 } 447 448 449 /** The PDB code for this protein structure. 450 * 451 * @return the PDB identifier 452 * @see #setIdCode(String) 453 */ 454 public String getIdCode() { 455 return idCode; 456 } 457 /** The PDB code for this protein structure. 458 * 459 * @param idCode the PDB identifier 460 * @see #getIdCode() 461 * 462 */ 463 public void setIdCode(String idCode) { 464 this.idCode = idCode; 465 } 466 467 468 public String getClassification() { 469 return classification; 470 } 471 472 public void setClassification(String classification) { 473 this.classification = classification; 474 } 475 476 /** 477 * Return the deposition date of the structure in the PDB. 478 * 479 * @return the deposition date 480 */ 481 public Date getDepDate() { 482 return depDate; 483 } 484 485 /** 486 * The deposition date of the structure in the PDB 487 * 488 * @param depDate the deposition date 489 */ 490 public void setDepDate(Date depDate) { 491 this.depDate = depDate; 492 } 493 494 /** 495 * Return the Set of ExperimentalTechniques, usually the set is of size 1 except for hybrid 496 * experimental techniques when the Set will contain 2 or more values 497 * @return the Set of ExperimentalTechniques or null if not set 498 */ 499 public Set<ExperimentalTechnique> getExperimentalTechniques() { 500 return techniques; 501 } 502 503 /** 504 * Adds the experimental technique to the set of experimental techniques of this header. 505 * Note that if input is not a recognised technique string then no errors will be produced but 506 * false will be returned 507 * @param techniqueStr 508 * @return true if the input corresponds to a recognised technique string (see {@link ExperimentalTechnique}) 509 * and it was not already present in the current set of ExperimentalTechniques 510 */ 511 public boolean setExperimentalTechnique(String techniqueStr) { 512 513 ExperimentalTechnique et = ExperimentalTechnique.getByName(techniqueStr); 514 515 if (et==null) return false; 516 517 if (techniques==null) { 518 techniques = EnumSet.of(et); 519 return true; 520 } else { 521 return techniques.add(et); 522 } 523 524 } 525 526 public PDBCrystallographicInfo getCrystallographicInfo() { 527 return crystallographicInfo; 528 } 529 530 public void setCrystallographicInfo(PDBCrystallographicInfo crystallographicInfo) { 531 this.crystallographicInfo = crystallographicInfo; 532 } 533 534 public float getResolution() { 535 return resolution; 536 } 537 538 public void setResolution(float resolution) { 539 this.resolution = resolution; 540 } 541 542 public float getRfree() { 543 return rFree; 544 } 545 546 public void setRfree(float rFree) { 547 this.rFree = rFree; 548 } 549 550 /** 551 * Return the latest modification date of the structure. 552 * 553 * @return the latest modification date 554 */ 555 public Date getModDate() { 556 return modDate; 557 } 558 559 /** 560 * The latest modification date of the structure. 561 * 562 * @param modDate the latest modification date 563 */ 564 public void setModDate(Date modDate) { 565 this.modDate = modDate; 566 } 567 568 /** 569 * Return the release date of the structure in the PDB. 570 * 571 * @return the release date 572 */ 573 public Date getRelDate() { 574 return relDate; 575 } 576 577 /** 578 * 579 * The release date of the structure in the PDB. 580 * 581 * @param relDate the release date 582 */ 583 public void setRelDate(Date relDate) { 584 this.relDate = relDate; 585 } 586 587 public String getTitle() { 588 return title; 589 } 590 public void setTitle(String title) { 591 this.title = title; 592 } 593 public String getDescription() { 594 return description; 595 } 596 public void setDescription(String description) { 597 this.description = description; 598 } 599 600 /** 601 * Return the names of the authors as listed in the AUTHORS section of a PDB file. 602 * Not necessarily the same authors as listed in the AUTH section of the primary citation! 603 * 604 * @return Authors as a string 605 */ 606 public String getAuthors() 607 { 608 return authors; 609 } 610 611 public void setAuthors(String authors) 612 { 613 this.authors = authors; 614 } 615 616 /** 617 * Return whether or not the entry has an associated journal article 618 * or publication. The JRNL section is not mandatory and thus may not be 619 * present. 620 * @return flag if a JournalArticle could be found. 621 */ 622 public boolean hasJournalArticle() { 623 return this.journalArticle != null; 624 } 625 626 /** 627 * Get the associated publication as defined by the JRNL records in a PDB 628 * file. 629 * @return a JournalArticle 630 */ 631 public JournalArticle getJournalArticle() { 632 return this.journalArticle; 633 } 634 635 /** 636 * Set the associated publication as defined by the JRNL records in a PDB 637 * file. 638 * @param journalArticle the article 639 */ 640 public void setJournalArticle(JournalArticle journalArticle) { 641 this.journalArticle = journalArticle; 642 } 643 644 /** 645 * Return the map of biological assemblies. The keys are the 646 * biological assembly identifiers (starting at 1). Non-numerical identifiers 647 * such as PAU or XAU are not supported. 648 * @return 649 */ 650 public Map<Integer,BioAssemblyInfo> getBioAssemblies() { 651 return bioAssemblies ; 652 } 653 654 public void setBioAssemblies(Map<Integer,BioAssemblyInfo> bioAssemblies) { 655 this.bioAssemblies = bioAssemblies; 656 } 657 658 /** 659 * Get the number of biological assemblies available in the PDB header 660 * @return 661 */ 662 public int getNrBioAssemblies() { 663 return this.bioAssemblies.size(); 664 } 665 666 public List<DatabasePdbrevRecord> getRevisionRecords() { 667 return revisionRecords; 668 } 669 670 public void setRevisionRecords(List<DatabasePdbrevRecord> revisionRecords) { 671 this.revisionRecords = revisionRecords; 672 } 673 674 /** 675 * @return the R-work for this structure. 676 */ 677 public float getRwork() { 678 return rWork; 679 } 680 681 /** 682 * @param rWork the R-work for this structure. 683 */ 684 public void setRwork(float rWork) { 685 this.rWork = rWork; 686 } 687}