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 at Oct 18, 2008 021 */ 022package org.biojava.nbio.structure.io; 023 024import org.biojava.nbio.structure.PDBStatus; 025import org.biojava.nbio.structure.PDBStatus.Status; 026import org.biojava.nbio.structure.Structure; 027import org.biojava.nbio.structure.StructureException; 028import org.biojava.nbio.structure.align.util.UserConfiguration; 029import org.biojava.nbio.core.util.FileDownloadUtils; 030import org.rcsb.mmtf.utils.CodecUtils; 031import org.biojava.nbio.core.util.InputStreamProvider; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import java.io.File; 036import java.io.IOException; 037import java.io.InputStream; 038import java.net.URL; 039import java.text.ParseException; 040import java.text.SimpleDateFormat; 041import java.util.*; 042 043/** 044 * Superclass for classes which download and interact with the PDB's FTP server, 045 * specifically {@link PDBFileReader} and {@link MMCIFFileReader}. The basic 046 * functionality of downloading structure files from the FTP site is gathered 047 * here, making the child classes responsible for only the specific paths and 048 * file formats needed. 049 * 050 * @author Spencer Bliven 051 * 052 */ 053public abstract class LocalPDBDirectory implements StructureIOFile { 054 055 private static final Logger logger = LoggerFactory.getLogger(LocalPDBDirectory.class); 056 057 /** 058 * The default server name, prefixed by the protocol string (http:// or ftp://). 059 * Note that we don't support file stamp retrieving for ftp protocol, thus some of the 060 * fetch modes will not work properly with ftp protocol 061 */ 062 public static final String DEFAULT_PDB_FILE_SERVER = "http://ftp.wwpdb.org"; 063 public static final String PDB_FILE_SERVER_PROPERTY = "PDB.FILE.SERVER"; 064 065 /** 066 * Behaviors for when an obsolete structure is requested. 067 * @author Spencer Bliven 068 * @see LocalPDBDirectory#setObsoleteBehavior(ObsoleteBehavior) 069 */ 070 public static enum ObsoleteBehavior { 071 /** Fetch the most recent version of the PDB entry. */ 072 FETCH_CURRENT, 073 /** Fetch the obsolete entry from the PDB archives. */ 074 FETCH_OBSOLETE, 075 /** Throw a StructureException for obsolete entries.*/ 076 THROW_EXCEPTION; 077 078 public static final ObsoleteBehavior DEFAULT=THROW_EXCEPTION; 079 } 080 081 /** 082 * Controls when the class should fetch files from the ftp server 083 * @author Spencer Bliven 084 * 085 */ 086 public static enum FetchBehavior { 087 /** Never fetch from the server; Throw errors for missing files */ 088 LOCAL_ONLY, 089 /** Fetch missing files from the server. Don't check for outdated files */ 090 FETCH_FILES, 091 /** 092 * Fetch missing files from the server, also fetch if file present but older than the 093 * server file. 094 * This requires always querying the server for the last modified time of the file, thus 095 * it adds an overhead to getting files from cache. 096 */ 097 FETCH_IF_OUTDATED, 098 /** 099 * Fetch missing files from the server. 100 * Also force the download of files older than {@value #LAST_REMEDIATION_DATE_STRING}. 101 */ 102 FETCH_REMEDIATED, 103 /** For every file, force downloading from the server */ 104 FORCE_DOWNLOAD; 105 106 public static final FetchBehavior DEFAULT = FETCH_REMEDIATED; 107 } 108 109 /** 110 * Date of the latest PDB file remediation 111 */ 112 public static final long LAST_REMEDIATION_DATE ; 113 private static final String LAST_REMEDIATION_DATE_STRING = "2011/07/12"; 114 115 static { 116 117 SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd", Locale.US); 118 119 long t = 0; 120 try { 121 Date d = formatter.parse(LAST_REMEDIATION_DATE_STRING); 122 t = d.getTime(); 123 } catch (ParseException e){ 124 logger.error("Unexpected error! could not parse LAST_REMEDIATION_DATE: "+e.getMessage()); 125 } 126 LAST_REMEDIATION_DATE = t; 127 } 128 129 protected static final String lineSplit = System.getProperty("file.separator"); 130 131 private File path; 132 private List<String> extensions; 133 134 /** 135 * The server name, prefixed by the protocol string (http:// or ftp://). 136 * Note that we don't support file stamp retrieving for ftp protocol, thus some of the 137 * fetch modes will not work properly with ftp protocol 138 */ 139 private String serverName; 140 141 private FileParsingParameters params; 142 143 private ObsoleteBehavior obsoleteBehavior; 144 private FetchBehavior fetchBehavior; 145 146 147 // Cache results of get*DirPath() 148 private String splitDirURL; // path on the server, starting with a slash and ending before the 2-char split directories 149 private String obsoleteDirURL; 150 private File splitDirPath; // path to the directory before the 2-char split 151 private File obsoleteDirPath; 152 153 /** 154 * Subclasses should provide default and single-string constructors. 155 * They should use {@link #addExtension(String)} to add one or more extensions. 156 * 157 * <p>If path is null, initialize using the system property/environment variable 158 * {@link UserConfiguration#PDB_DIR}. 159 * @param path Path to the PDB file directory 160 */ 161 public LocalPDBDirectory(String path) { 162 extensions = new ArrayList<String>(); 163 164 params = new FileParsingParameters(); 165 166 if( path == null) { 167 UserConfiguration config = new UserConfiguration(); 168 path = config.getPdbFilePath(); 169 logger.debug("Initialising from system property/environment variable to path: {}", path); 170 } else { 171 path = FileDownloadUtils.expandUserHome(path); 172 logger.debug("Initialising with path {}", path); 173 } 174 this.path = new File(path); 175 176 this.serverName = getServerName(); 177 178 // Initialize splitDirURL,obsoleteDirURL,splitDirPath,obsoleteDirPath 179 initPaths(); 180 181 fetchBehavior = FetchBehavior.DEFAULT; 182 obsoleteBehavior = ObsoleteBehavior.DEFAULT; 183 } 184 185 public LocalPDBDirectory() { 186 this(null); 187 } 188 189 /** 190 * Sets the path for the directory where PDB files are read/written 191 */ 192 public void setPath(String p){ 193 path = new File(FileDownloadUtils.expandUserHome(p)) ; 194 initPaths(); 195 } 196 197 /** 198 * Returns the path value. 199 * @return a String representing the path value 200 * @see #setPath 201 * 202 */ 203 public String getPath() { 204 return path.toString() ; 205 } 206 207 /** define supported file extensions 208 * compressed extensions .Z,.gz do not need to be specified 209 * they are dealt with automatically. 210 */ 211 @Override 212 public void addExtension(String s){ 213 //System.out.println("add Extension "+s); 214 extensions.add(s); 215 } 216 217 @Override 218 public List<String> getExtensions() { 219 return Collections.unmodifiableList(extensions); 220 } 221 222 /** clear the supported file extensions 223 * 224 */ 225 public void clearExtensions(){ 226 extensions.clear(); 227 } 228 229 @Override 230 public void setFileParsingParameters(FileParsingParameters params){ 231 this.params= params; 232 } 233 234 @Override 235 public FileParsingParameters getFileParsingParameters(){ 236 return params; 237 } 238 239 /** 240 * <b>[Optional]</b> This method changes the behavior when obsolete entries 241 * are requested. Current behaviors are: 242 * <ul> 243 * <li>{@link ObsoleteBehavior#THROW_EXCEPTION THROW_EXCEPTION} 244 * Throw a {@link StructureException} (the default) 245 * <li>{@link ObsoleteBehavior#FETCH_OBSOLETE FETCH_OBSOLETE} 246 * Load the requested ID from the PDB's obsolete repository 247 * <li>{@link ObsoleteBehavior#FETCH_CURRENT FETCH_CURRENT} 248 * Load the most recent version of the requested structure 249 * 250 * <p>This setting may be silently ignored by implementations which do not have 251 * access to the server to determine whether an entry is obsolete, such as 252 * if {@link #isAutoFetch()} is false. Note that an obsolete entry may still be 253 * returned even this is FETCH_CURRENT if the entry is found locally. 254 * 255 * @param fetchFileEvenIfObsolete Whether to fetch obsolete records 256 * @see #setFetchCurrent(boolean) 257 * @since 4.0.0 258 */ 259 public void setObsoleteBehavior(ObsoleteBehavior behavior) { 260 obsoleteBehavior = behavior; 261 } 262 263 /** 264 * Returns how this instance deals with obsolete entries. Note that this 265 * setting may be ignored by some implementations or in some situations, 266 * such as when {@link #isAutoFetch()} is false. 267 * 268 * <p>For most implementations, the default value is 269 * {@link ObsoleteBehavior#THROW_EXCEPTION THROW_EXCEPTION}. 270 * 271 * @return The ObsoleteBehavior 272 * @since 4.0.0 273 */ 274 public ObsoleteBehavior getObsoleteBehavior() { 275 return obsoleteBehavior; 276 } 277 278 /** 279 * Get the behavior for fetching files from the server 280 * @return 281 */ 282 public FetchBehavior getFetchBehavior() { 283 return fetchBehavior; 284 } 285 /** 286 * Set the behavior for fetching files from the server. 287 * This replaces the {@link #setAutoFetch(boolean)} method with a more 288 * extensive set of options. 289 * @param fetchBehavior 290 */ 291 public void setFetchBehavior(FetchBehavior fetchBehavior) { 292 this.fetchBehavior = fetchBehavior; 293 } 294 295 296 @Override 297 public Structure getStructure(String filename) throws IOException 298 { 299 filename = FileDownloadUtils.expandUserHome(filename); 300 File f = new File(filename); 301 return getStructure(f); 302 303 } 304 305 public Structure getStructure(URL u) throws IOException{ 306 InputStreamProvider isp = new InputStreamProvider(); 307 InputStream inStream = isp.getInputStream(u); 308 return getStructure(inStream); 309 } 310 311 @Override 312 public Structure getStructure(File filename) throws IOException { 313 InputStreamProvider isp = new InputStreamProvider(); 314 315 InputStream inStream = isp.getInputStream(filename); 316 317 return getStructure(inStream); 318 } 319 320 321 @Override 322 public Structure getStructureById(String pdbId) throws IOException { 323 InputStream inStream = getInputStream(pdbId); 324 325 return getStructure(inStream); 326 } 327 328 /** 329 * Handles the actual parsing of the file into a Structure object. 330 * @param inStream 331 * @return 332 * @throws IOException 333 */ 334 public abstract Structure getStructure(InputStream inStream) throws IOException; 335 336 /** 337 * Load or download the specified structure and return it as an InputStream 338 * for direct parsing. 339 * @param pdbId 340 * @return 341 * @throws IOException 342 */ 343 protected InputStream getInputStream(String pdbId) throws IOException{ 344 345 if ( pdbId.length() != 4) 346 throw new IOException("The provided ID does not look like a PDB ID : " + pdbId); 347 348 // Check existing 349 File file = downloadStructure(pdbId); 350 351 if(!file.exists()) { 352 throw new IOException("Structure "+pdbId+" not found and unable to download."); 353 } 354 355 InputStreamProvider isp = new InputStreamProvider(); 356 357 InputStream inputStream = isp.getInputStream(file); 358 359 return inputStream; 360 } 361 362 /** 363 * Download a structure, but don't parse it yet or store it in memory. 364 * 365 * Used to pre-fetch large numbers of structures. 366 * @param pdbId 367 * @throws IOException 368 */ 369 public void prefetchStructure(String pdbId) throws IOException { 370 if ( pdbId.length() != 4) 371 throw new IOException("The provided ID does not look like a PDB ID : " + pdbId); 372 373 // Check existing 374 File file = downloadStructure(pdbId); 375 376 if(!file.exists()) { 377 throw new IOException("Structure "+pdbId+" not found and unable to download."); 378 } 379 } 380 381 /** 382 * Attempts to delete all versions of a structure from the local directory. 383 * @param pdbId 384 * @return True if one or more files were deleted 385 */ 386 public boolean deleteStructure(String pdbId){ 387 boolean deleted = false; 388 // Force getLocalFile to check in obsolete locations 389 ObsoleteBehavior obsolete = getObsoleteBehavior(); 390 setObsoleteBehavior(ObsoleteBehavior.FETCH_OBSOLETE); 391 392 try { 393 File existing = getLocalFile(pdbId); 394 while(existing != null) { 395 assert(existing.exists()); // should exist unless concurrency problems 396 397 if( getFetchBehavior() == FetchBehavior.LOCAL_ONLY) { 398 throw new RuntimeException("Refusing to delete from LOCAL_ONLY directory"); 399 } 400 401 // delete file 402 boolean success = existing.delete(); 403 if(success) { 404 logger.info("Deleting "+existing.getAbsolutePath()); 405 } 406 deleted = deleted || success; 407 408 // delete parent if empty 409 File parent = existing.getParentFile(); 410 if(parent != null) { 411 success = parent.delete(); 412 if(success) { 413 logger.info("Deleting "+parent.getAbsolutePath()); 414 } 415 } 416 417 existing = getLocalFile(pdbId); 418 } 419 return deleted; 420 421 } finally { 422 setObsoleteBehavior(obsolete); 423 } 424 } 425 426 /** 427 * Downloads an MMCIF file from the PDB to the local path 428 * @param pdbId 429 * @return The file, or null if it was unavailable for download 430 * @throws IOException for errors downloading or writing, or if the 431 * fetchBehavior is {@link FetchBehavior#LOCAL_ONLY} 432 */ 433 protected File downloadStructure(String pdbId) throws IOException{ 434 if ( pdbId.length() != 4) 435 throw new IOException("The provided ID does not look like a PDB ID : " + pdbId); 436 437 // decide whether download is required 438 File existing = getLocalFile(pdbId); 439 switch(fetchBehavior) { 440 case LOCAL_ONLY: 441 if( existing == null ) { 442 throw new IOException(String.format("Structure %s not found in %s " 443 + "and configured not to download.",pdbId,getPath())); 444 } else { 445 return existing; 446 } 447 case FETCH_FILES: 448 // Use existing if present 449 if( existing != null) { 450 return existing; 451 } 452 // existing is null, downloadStructure(String,String,boolean,File) will download it 453 break; 454 case FETCH_IF_OUTDATED: 455 // here existing can be null or not: 456 // existing == null : downloadStructure(String,String,boolean,File) will download it 457 // existing != null : downloadStructure(String,String,boolean,File) will check its date and download if older 458 break; 459 case FETCH_REMEDIATED: 460 // Use existing if present and recent enough 461 if( existing != null) { 462 long lastModified = existing.lastModified(); 463 464 if (lastModified < LAST_REMEDIATION_DATE) { 465 // the file is too old, replace with newer version 466 logger.warn("Replacing file {} with latest remediated (remediation of {}) file from PDB.", 467 existing, LAST_REMEDIATION_DATE_STRING); 468 existing = null; 469 break; 470 } else { 471 return existing; 472 } 473 } 474 case FORCE_DOWNLOAD: 475 // discard the existing file to force redownload 476 existing = null; // downloadStructure(String,String,boolean,File) will download it 477 break; 478 } 479 480 // Force the download now 481 if(obsoleteBehavior == ObsoleteBehavior.FETCH_CURRENT) { 482 String current = PDBStatus.getCurrent(pdbId); 483 484 if(current == null) { 485 // either an error or there is not current entry 486 current = pdbId; 487 } 488 return downloadStructure(current, splitDirURL,false, existing); 489 } else if(obsoleteBehavior == ObsoleteBehavior.FETCH_OBSOLETE 490 && PDBStatus.getStatus(pdbId) == Status.OBSOLETE) { 491 return downloadStructure(pdbId, obsoleteDirURL, true, existing); 492 } else { 493 return downloadStructure(pdbId, splitDirURL, false, existing); 494 } 495 } 496 497 /** 498 * Download a file from the ftp server, replacing any existing files if needed 499 * @param pdbId PDB ID 500 * @param pathOnServer Path on the FTP server, e.g. data/structures/divided/pdb 501 * @param obsolete Whether or not file should be saved to the obsolete location locally 502 * @param existingFile if not null and checkServerFileDate is true, the last modified date of the 503 * server file and this file will be compared to decide whether to download or not 504 * @return 505 * @throws IOException 506 */ 507 private File downloadStructure(String pdbId, String pathOnServer, boolean obsolete, File existingFile) 508 throws IOException{ 509 510 File dir = getDir(pdbId,obsolete); 511 File realFile = new File(dir,getFilename(pdbId)); 512 513 String ftp; 514 515 if (getFilename(pdbId).endsWith(".mmtf.gz")){ 516 ftp = CodecUtils.getMmtfEntryUrl(pdbId, true, false); 517 } else { 518 ftp = String.format("%s%s/%s/%s", 519 serverName, pathOnServer, pdbId.substring(1,3).toLowerCase(), getFilename(pdbId)); 520 } 521 522 URL url = new URL(ftp); 523 524 Date serverFileDate = null; 525 if (existingFile!=null) { 526 527 serverFileDate = getLastModifiedTime(url); 528 529 if (serverFileDate!=null) { 530 if (existingFile.lastModified()>=serverFileDate.getTime()) { 531 return existingFile; 532 } else { 533 // otherwise we go ahead and download, warning about it first 534 logger.warn("File {} is outdated, will download new one from PDB (updated on {})", 535 existingFile, serverFileDate.toString()); 536 } 537 } else { 538 logger.warn("Could not determine if file {} is outdated (could not get timestamp from server). Will force redownload", existingFile); 539 } 540 } 541 542 logger.info("Fetching " + ftp); 543 logger.info("Writing to "+ realFile); 544 545 FileDownloadUtils.downloadFile(url, realFile); 546 547 // Commented out following code used for setting the modified date to the downloaded file - JD 2015-01-15 548 // The only reason to have it was in order to get an rsync-like behavior, respecting the timestamps 549 // but the issue is that it would make the FETCH_REMEDIATED mode redownload files with timestamps before 550 // the remediation. 551 //if (serverFileDate==null) 552 // serverFileDate = getLastModifiedTime(url); 553 // 554 //if (serverFileDate!=null) { 555 // logger.debug("Setting modified time of downloaded file {} to {}",realFile,serverFileDate.toString()); 556 // realFile.setLastModified(serverFileDate.getTime()); 557 //} else { 558 // logger.warn("Unknown modified time of file {}, will set its modified time to now.", ftp); 559 //} 560 561 562 return realFile; 563 } 564 565 /** 566 * Get the last modified time of the file in given url by retrieveing the "Last-Modified" header. 567 * Note that this only works for http URLs 568 * @param url 569 * @return the last modified date or null if it couldn't be retrieved (in that case a warning will be logged) 570 */ 571 private Date getLastModifiedTime(URL url) { 572 573 // see http://stackoverflow.com/questions/2416872/how-do-you-obtain-modified-date-from-a-remote-file-java 574 Date date = null; 575 try { 576 String lastModified = url.openConnection().getHeaderField("Last-Modified"); 577 logger.debug("Last modified date of server file ({}) is {}",url.toString(),lastModified); 578 579 580 if (lastModified!=null) { 581 582 try { 583 date = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH).parse(lastModified); 584 } catch (ParseException e) { 585 logger.warn("Could not parse last modified time from string '{}', no last modified time available for file {}", 586 lastModified, url.toString()); 587 // this will return null 588 } 589 590 } 591 } catch (IOException e) { 592 logger.warn("Problems while retrieving last modified time for file {}", url.toString()); 593 } 594 return date; 595 596 } 597 598 /** 599 * Gets the directory in which the file for a given MMCIF file would live, 600 * creating it if necessary. 601 * 602 * The obsolete parameter is necessary to avoid additional server queries. 603 * @param pdbId 604 * @param obsolete Whether the pdbId is obsolete or not 605 * @return File pointing to the directory, 606 */ 607 protected File getDir(String pdbId, boolean obsolete) { 608 609 File dir = null; 610 611 if (obsolete) { 612 // obsolete is always split 613 String middle = pdbId.substring(1,3).toLowerCase(); 614 dir = new File(obsoleteDirPath, middle); 615 } else { 616 String middle = pdbId.substring(1,3).toLowerCase(); 617 dir = new File(splitDirPath, middle); 618 } 619 620 621 if (!dir.exists()) { 622 boolean success = dir.mkdirs(); 623 if (!success) logger.error("Could not create mmCIF dir {}",dir.toString()); 624 } 625 626 return dir; 627 } 628 629 /** 630 * Searches for previously downloaded files 631 * @param pdbId 632 * @return A file pointing to the existing file, or null if not found 633 */ 634 public File getLocalFile(String pdbId) { 635 636 // Search for existing files 637 638 // Search directories: 639 // 1) LOCAL_MMCIF_SPLIT_DIR/<middle>/(pdb)?<pdbId>.<ext> 640 // 2) LOCAL_MMCIF_ALL_DIR/<middle>/(pdb)?<pdbId>.<ext> 641 LinkedList<File> searchdirs = new LinkedList<File>(); 642 String middle = pdbId.substring(1,3).toLowerCase(); 643 644 File splitdir = new File(splitDirPath, middle); 645 searchdirs.add(splitdir); 646 // Search obsolete files if requested 647 if(getObsoleteBehavior() == ObsoleteBehavior.FETCH_OBSOLETE) { 648 File obsdir = new File(obsoleteDirPath,middle); 649 searchdirs.add(obsdir); 650 } 651 652 // valid prefixes before the <pdbId> in the filename 653 String[] prefixes = new String[] {"", "pdb"}; 654 655 for( File searchdir :searchdirs){ 656 for( String prefix : prefixes) { 657 for(String ex : getExtensions() ){ 658 File f = new File(searchdir,prefix + pdbId.toLowerCase() + ex) ; 659 if ( f.exists()) { 660 return f; 661 } 662 } 663 } 664 } 665 //Not found 666 return null; 667 } 668 669 protected boolean checkFileExists(String pdbId){ 670 File path = getLocalFile(pdbId); 671 if ( path != null) 672 return true; 673 return false; 674 } 675 676 /** 677 * Return the String with the PDB server name, including the leading protocol 678 * String (http:// or ftp://). 679 * The server name will be by default the value {@value #DEFAULT_PDB_FILE_SERVER} or the one 680 * read from system property {@value #PDB_FILE_SERVER_PROPERTY} 681 * 682 * @return the server name including the leading protocol string 683 */ 684 public static String getServerName() { 685 String name = System.getProperty(PDB_FILE_SERVER_PROPERTY); 686 687 if ( name == null || name.trim().isEmpty()) { 688 name = DEFAULT_PDB_FILE_SERVER; 689 logger.debug("Using default PDB file server {}",name); 690 } else { 691 if (!name.startsWith("http://") && !name.startsWith("ftp://")) { 692 logger.warn("Server name {} read from system property {} does not have a leading protocol string. Adding http:// to it", name, PDB_FILE_SERVER_PROPERTY); 693 name = "http://"+name; 694 } 695 logger.info("Using PDB file server {} read from system property {}", name, PDB_FILE_SERVER_PROPERTY); 696 } 697 return name; 698 } 699 700 /** 701 * Should be called whenever any of the path variables change. 702 * Thus, if {@link getSplitDirPath()} or {@link getObsoleteDirPath()} 703 * depend on anything, they should call this function when that thing 704 * changes (possibly including at the end of the constructor). 705 */ 706 protected void initPaths() { 707 // Hand-rolled String.join(), for java 6 708 String[] split = getSplitDirPath(); 709 String[] obsolete = getObsoleteDirPath(); 710 711 //URLs are joined with '/' 712 StringBuilder splitURL = new StringBuilder("/pub/pdb"); 713 for(int i=0;i<split.length;i++) { 714 splitURL.append("/"); 715 splitURL.append(split[i]); 716 } 717 StringBuilder obsoleteURL = new StringBuilder("/pub/pdb"); 718 for(int i=0;i<obsolete.length;i++) { 719 obsoleteURL.append("/"); 720 obsoleteURL.append(obsolete[i]); 721 } 722 723 splitDirURL = splitURL.toString(); 724 obsoleteDirURL = obsoleteURL.toString(); 725 726 727 //Files join themselves iteratively 728 splitDirPath = path; 729 for(int i=0;i<split.length;i++) { 730 splitDirPath = new File(splitDirPath,split[i]); 731 } 732 obsoleteDirPath = path; 733 for(int i=0;i<obsolete.length;i++) { 734 obsoleteDirPath = new File(obsoleteDirPath,obsolete[i]); 735 } 736 } 737 738 /** 739 * Converts a PDB ID into a filename with the proper extension 740 * @param pdbId 741 * @return The filename, e.g. "4hhb.pdb.gz" 742 */ 743 protected abstract String getFilename(String pdbId); 744 745 /** 746 * Location of split files within the directory, as an array of paths. 747 * These will be joined with either slashes (for the URL) or the file 748 * separator (for directories). The returned results should be constant, 749 * to allow for caching. 750 * @return A list of directories, relative to the /pub/pdb directory on the server 751 */ 752 protected abstract String[] getSplitDirPath(); 753 /** 754 * Location of obsolete files within the directory, as an array of paths. 755 * These will be joined with either slashes (for the URL) or the file 756 * separator (for directories). The returned results should be constant, 757 * to allow for caching. 758 * @return A list of directories, relative to the /pub/pdb directory on the server 759 */ 760 protected abstract String[] getObsoleteDirPath(); 761}