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 Nov 2, 2009 021 * Author: Andreas Prlic 022 * 023 */ 024 025package org.biojava.nbio.structure.align.ce; 026 027 028import org.biojava.nbio.structure.Atom; 029import org.biojava.nbio.structure.Structure; 030import org.biojava.nbio.structure.StructureException; 031import org.biojava.nbio.structure.StructureTools; 032import org.biojava.nbio.structure.align.StructureAlignment; 033import org.biojava.nbio.structure.align.model.AFPChain; 034import org.biojava.nbio.structure.align.util.*; 035import org.biojava.nbio.structure.align.xml.AFPChainXMLConverter; 036import org.biojava.nbio.structure.io.LocalPDBDirectory.FetchBehavior; 037import org.biojava.nbio.structure.io.PDBFileReader; 038 039import java.beans.Introspector; 040import java.io.*; 041import java.lang.reflect.InvocationTargetException; 042import java.net.URL; 043import java.util.ArrayList; 044import java.util.Iterator; 045import java.util.List; 046import java.util.stream.Stream; 047 048 049/** 050 * Base class for a new structure alignment CLI. 051 * 052 * <p>To add a new StructureAlignment with a CLI similar to CE or FATCAT, 053 * <ol> 054 * <li>Implement StructureAlignment with the main algorithm 055 * <li>Implement ConfigStrucAligParams. This provides the parameters for the GUI. 056 * <li>Subclass StartupParameters (can be an inner class) with the same parameters 057 * described in the ConfigStrucAligParams. 058 * <li>Subclass AbstractUserArgumentProcessor, with the getStartupParams() method 059 * returning a fresh instance of the custom StartupParameters 060 * <li>Implement the getParameters() method to copy values from the StartupParameters 061 * to the ConfigStrucAligParams. 062 * </ol> 063 * 064 * <p>Note that reflection is used in a number of places, so the CLI argument names 065 * must match the get/set functions in both parameter beans. 066 * <ul> 067 * <li>AlignmentGUI automatically takes parameter names and types from the 068 * ConfigStrucAligParams 069 * <li>AbstractUserArgumentProcessor also takes parameter names and help descriptions 070 * from ConfigStrucAligParams, but it saves arguments to the StartupParameter 071 * bean. 072 * </ul> 073 * This means that both beans should be kept in sync. 074 * 075 * @author Andreas 076 * @author Spencer 077 * 078 */ 079public abstract class AbstractUserArgumentProcessor implements UserArgumentProcessor { 080 081 public static String newline = System.getProperty("line.separator"); 082 083 protected StartupParameters params ; 084 085 public static final List<String> mandatoryArgs= new ArrayList<String>(); 086 087 protected AbstractUserArgumentProcessor(){ 088 params = getStartupParametersInstance(); 089 } 090 091 /** 092 * StartupParameters is a bean to store all the possible 093 * command line parameters. 094 * 095 * The `StartupParameter` class contains the basic set of CLI parameters 096 * common to all `StructureAligmnent`s. This method should return a subclass 097 * of StartupParameters which has been extended to store values for all 098 * additional parameters. 099 * @return A new instance of the correct StartupParameters subclass 100 */ 101 protected abstract StartupParameters getStartupParametersInstance(); 102 103 public abstract StructureAlignment getAlgorithm(); 104 public abstract Object getParameters(); 105 public abstract String getDbSearchLegend(); 106 107 @Override 108 public void process(String[] argv){ 109 110 printAboutMe(); 111 112// if(argv.length == 0 ) { 113// System.out.println(printHelp()); 114// return; 115// } 116 117 for (int i = 0 ; i < argv.length; i++){ 118 String arg = argv[i]; 119 120 // help string 121 if(arg.equalsIgnoreCase("-h") || arg.equalsIgnoreCase("-help") 122 || arg.equalsIgnoreCase("--help") ) 123 { 124 System.out.println(printHelp()); 125 return; 126 } 127 // version 128 if(arg.equalsIgnoreCase("-version") || arg.equalsIgnoreCase("--version")) { 129 StructureAlignment alg = getAlgorithm(); 130 System.out.println(alg.getAlgorithmName() + " v." + alg.getVersion() ); 131 return; 132 } 133 134 String value = null; 135 if ( i < argv.length -1) 136 value = argv[i+1]; 137 138 // if value starts with - then the arg does not have a value. 139 if (value != null && value.startsWith("-")) 140 value = null; 141 else 142 i++; 143 144 145 String[] tmp = {arg,value}; 146 147 //System.out.println(arg + " " + value); 148 149 try { 150 151 CliTools.configureBean(params, tmp); 152 153 } catch (ConfigurationException e){ 154 System.err.println("Error: "+e.getLocalizedMessage()); 155 System.exit(1); return; 156 } 157 } 158 159 if ( params.getPdbFilePath() != null){ 160 System.setProperty(UserConfiguration.PDB_DIR,params.getPdbFilePath()); 161 } 162 163 if ( params.getCacheFilePath() != null){ 164 System.setProperty(UserConfiguration.PDB_CACHE_DIR,params.getCacheFilePath()); 165 } 166 167 if ( params.isShowMenu()){ 168 System.err.println("showing menu..."); 169 try { 170 GuiWrapper.showAlignmentGUI(); 171 } catch (Exception e){ 172 System.err.println(e.getMessage()); 173 e.printStackTrace(); 174 } 175 } 176 177 String pdb1 = params.getPdb1(); 178 String file1 = params.getFile1(); 179 180 181 try { 182 if (pdb1 != null || file1 != null){ 183 runPairwise(); 184 return; 185 } 186 187 188 } catch (ConfigurationException e) { 189 System.err.println(e.getLocalizedMessage()); 190 System.exit(1); return; 191 } 192 193 System.out.println(printHelp()); 194 System.err.println("Error: insufficient arguments."); 195 System.exit(1); return; 196 } 197 198 199 200 201 202 public static void printAboutMe() { 203 try { 204 ResourceManager about = ResourceManager.getResourceManager("about"); 205 206 String version = about.getString("project_version"); 207 String build = about.getString("build"); 208 209 System.out.println("Protein Comparison Tool " + version + " " + build); 210 } catch (Exception e){ 211 e.printStackTrace(); 212 } 213 214 215 } 216 217 private void runAlignPairs(AtomCache cache, String alignPairs, 218 String outputFile) { 219 try { 220 File f = new File(alignPairs); 221 222 BufferedReader is = new BufferedReader (new InputStreamReader(new FileInputStream(f))); 223 224 BufferedWriter out = new BufferedWriter(new FileWriter(outputFile, true)); 225 226 StructureAlignment algorithm = getAlgorithm(); 227 228 String header = "# algorithm:" + algorithm.getAlgorithmName(); 229 out.write(header); 230 out.write(newline); 231 232 out.write("#Legend: " + newline ); 233 String legend = getDbSearchLegend(); 234 out.write(legend + newline ); 235 System.out.println(legend); 236 String line = null; 237 while ( (line = is.readLine()) != null){ 238 if ( line.startsWith("#")) 239 continue; 240 241 String[] spl = line.split(" "); 242 243 if ( spl.length != 2) { 244 System.err.println("wrongly formattted line. Expected format: 4hhb.A 4hhb.B but found " + line); 245 continue; 246 } 247 248 String pdb1 = spl[0]; 249 String pdb2 = spl[1]; 250 251 252 Structure structure1 = cache.getStructure(pdb1); 253 Structure structure2 = cache.getStructure(pdb2); 254 255 Atom[] ca1; 256 Atom[] ca2; 257 258 259 ca1 = StructureTools.getRepresentativeAtomArray(structure1); 260 ca2 = StructureTools.getRepresentativeAtomArray(structure2); 261 262 Object jparams = getParameters(); 263 264 AFPChain afpChain; 265 266 afpChain = algorithm.align(ca1, ca2, jparams); 267 afpChain.setName1(pdb1); 268 afpChain.setName2(pdb2); 269 270 String result = getDbSearchResult(afpChain); 271 out.write(result); 272 System.out.print(result); 273 274 checkWriteFile(afpChain,ca1,ca2,true); 275 } 276 277 out.close(); 278 is.close(); 279 } catch(Exception e){ 280 e.printStackTrace(); 281 } 282 } 283 284 285 private void runPairwise() throws ConfigurationException{ 286 287 String name1 = params.getPdb1(); 288 String file1 = params.getFile1(); 289 290 if ( name1 == null && file1 == null){ 291 throw new ConfigurationException("You did not specify the -pdb1 or -file1 parameter. Can not find query PDB id for alignment."); 292 } 293 294 if ( file1 == null) { 295 if ( name1.length() < 4) { 296 throw new ConfigurationException("-pdb1 does not look like a PDB ID. Please specify PDB code or PDB.chainName."); 297 } 298 } 299 300 String name2 = params.getPdb2(); 301 String file2 = params.getFile2(); 302 if ( name2 == null && file2 == null ){ 303 throw new ConfigurationException("You did not specify the -pdb2 or -file2 parameter. Can not find target PDB id for alignment."); 304 } 305 306 if ( file2 == null ){ 307 if ( name2.length() < 4) { 308 throw new ConfigurationException("-pdb2 does not look like a PDB ID. Please specify PDB code or PDB.chainName."); 309 } 310 } 311 312 // first load two example structures 313 314 Structure structure1 = null; 315 Structure structure2 = null; 316 317 String path = params.getPdbFilePath(); 318 319 if ( file1 == null || file2 == null) { 320 if ( path == null){ 321 UserConfiguration c = new UserConfiguration(); 322 path = c.getPdbFilePath(); 323 System.err.println("You did not specify the -pdbFilePath parameter. Defaulting to "+path+"."); 324 } 325 326 AtomCache cache = new AtomCache(path, path); 327 if(params.isAutoFetch()) { 328 cache.setFetchBehavior(FetchBehavior.DEFAULT); 329 } else { 330 cache.setFetchBehavior(FetchBehavior.LOCAL_ONLY); 331 } 332 structure1 = getStructure(cache, name1, file1); 333 structure2 = getStructure(cache, name2, file2); 334 } else { 335 336 structure1 = getStructure(null, name1, file1); 337 structure2 = getStructure(null, name2, file2); 338 } 339 340 341 342 if ( structure1 == null){ 343 System.err.println("structure 1 is null, can't run alignment."); 344 System.exit(1); return; 345 } 346 347 if ( structure2 == null){ 348 System.err.println("structure 2 is null, can't run alignment."); 349 System.exit(1); return; 350 } 351 352 if ( name1 == null) { 353 name1 = structure1.getName(); 354 } 355 if ( name2 == null) { 356 name2 = structure2.getName(); 357 } 358 359 // default: new: 360 // 1buz - 1ali : time: 8.3s eqr 68 rmsd 3.1 score 161 | time 6.4 eqr 58 rmsd 3.0 scre 168 361 // 5pti - 1tap : time: 6.2s eqr 48 rmsd 2.67 score 164 | time 5.2 eqr 49 rmsd 2.9 score 151 362 // 1cdg - 8tim 363 // 1jbe - 1ord 364 // 1nbw.A - 1kid 365 // 1t4y - 1rp5 366 367 368 try { 369 370 Atom[] ca1; 371 Atom[] ca2; 372 373 ca1 = StructureTools.getRepresentativeAtomArray(structure1); 374 ca2 = StructureTools.getRepresentativeAtomArray(structure2); 375 376 StructureAlignment algorithm = getAlgorithm(); 377 Object jparams = getParameters(); 378 379 AFPChain afpChain; 380 381 afpChain = algorithm.align(ca1, ca2, jparams); 382 afpChain.setName1(name1); 383 afpChain.setName2(name2); 384 385 if ( params.isShow3d()){ 386 387 if (! GuiWrapper.isGuiModuleInstalled()) { 388 System.err.println("The biojava-structure-gui module is not installed. Please install!"); 389 } else { 390 391 try { 392 393 Object jmol = GuiWrapper.display(afpChain,ca1,ca2); 394 395 GuiWrapper.showAlignmentImage(afpChain, ca1,ca2,jmol); 396 397 } catch (Exception e){ 398 399 System.err.println(e.getMessage()); 400 e.printStackTrace(); 401 } 402 //StructureAlignmentJmol jmol = algorithm.display(afpChain,ca1,ca2,hetatms1, nucs1, hetatms2, nucs2); 403 404 //String result = afpChain.toFatcat(ca1, ca2); 405 //String rot = afpChain.toRotMat(); 406 407 //DisplayAFP.showAlignmentImage(afpChain, ca1,ca2,jmol); 408 } 409 } 410 411 412 checkWriteFile(afpChain,ca1, ca2, false); 413 414 415 416 417 if ( params.isPrintXML()){ 418 String fatcatXML = AFPChainXMLConverter.toXML(afpChain,ca1,ca2); 419 System.out.println(fatcatXML); 420 } 421 if ( params.isPrintFatCat()) { 422 // default output is to XML on sysout... 423 System.out.println(afpChain.toFatcat(ca1, ca2)); 424 } 425 if ( params. isPrintCE()){ 426 System.out.println(afpChain.toCE(ca1, ca2)); 427 } 428 429 } catch (IOException e) { 430 e.printStackTrace(); 431 System.exit(1); return; 432 } catch (ClassNotFoundException e) { 433 e.printStackTrace(); 434 System.exit(1); return; 435 } catch (NoSuchMethodException e) { 436 e.printStackTrace(); 437 System.exit(1); return; 438 } catch (InvocationTargetException e) { 439 e.printStackTrace(); 440 System.exit(1); return; 441 } catch (IllegalAccessException e) { 442 e.printStackTrace(); 443 System.exit(1); return; 444 } catch (StructureException e) { 445 e.printStackTrace(); 446 System.exit(1); return; 447 } 448 } 449 450 /** 451 * check if the result should be written to the local file system 452 * 453 * @param afpChain 454 * @param ca1 455 * @param ca2 456 * @throws IOException If an error occurs when writing the afpChain to XML 457 * @throws ClassNotFoundException If an error occurs when invoking jmol 458 * @throws NoSuchMethodException If an error occurs when invoking jmol 459 * @throws InvocationTargetException If an error occurs when invoking jmol 460 * @throws IllegalAccessException If an error occurs when invoking jmol 461 * @throws StructureException 462 */ 463 private void checkWriteFile( AFPChain afpChain, Atom[] ca1, Atom[] ca2, boolean dbsearch) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, StructureException 464 { 465 String output = null; 466 if ( params.isOutputPDB()){ 467 if (! GuiWrapper.isGuiModuleInstalled()) { 468 System.err.println("The biojava-structure-gui module is not installed. Please install!"); 469 output = AFPChainXMLConverter.toXML(afpChain,ca1,ca2); 470 } else { 471 472 Structure tmp = AFPAlignmentDisplay.createArtificalStructure(afpChain, ca1, ca2); 473 output = "TITLE " + afpChain.getAlgorithmName() + " " + afpChain.getVersion() + " "; 474 output += afpChain.getName1() + " vs. " + afpChain.getName2(); 475 output += newline; 476 output += tmp.toPDB(); 477 } 478 } else if ( params.getOutFile() != null) { 479 // output by default is XML 480 // write the XML to a file... 481 output = AFPChainXMLConverter.toXML(afpChain,ca1,ca2); 482 483 } else if ( params.getSaveOutputDir() != null){ 484 output = AFPChainXMLConverter.toXML(afpChain,ca1,ca2); 485 } 486 487 // no output requested. 488 if ( output == null) 489 return; 490 491 String fileName = null; 492 493 if ( dbsearch ){ 494 if ( params.getSaveOutputDir() != null) { 495 496 // we currently don't have a naming convention for how to store results for custom files 497 // they will be re-created on the fly 498 if ( afpChain.getName1().startsWith("file:") || afpChain.getName2().startsWith("file:")) 499 return; 500 fileName = params.getSaveOutputDir(); 501 fileName += getAutoFileName(afpChain); 502 503 } else { 504 return; 505 } 506 507 // 508 //else { 509 // fileName = getAutoFileName(afpChain); 510 //} 511 } else 512 513 if ( params.getOutFile() != null) { 514 fileName = params.getOutFile(); 515 } 516 517 if (fileName == null) { 518 System.err.println("Can't write outputfile. Either provide a filename using -outFile or set -autoOutputFile to true ."); 519 System.exit(1); return; 520 } 521 //System.out.println("writing results to " + fileName + " " + params.getSaveOutputDir()); 522 523 FileOutputStream out; // declare a file output object 524 PrintStream p; // declare a print stream object 525 526 // Create a new file output stream 527 out = new FileOutputStream(fileName); 528 529 // Connect print stream to the output stream 530 p = new PrintStream( out ); 531 532 p.println (output); 533 534 p.close(); 535 } 536 537 private String getAutoFileName(AFPChain afpChain){ 538 String fileName =afpChain.getName1()+"_" + afpChain.getName2()+"_"+afpChain.getAlgorithmName(); 539 540 if (params.isOutputPDB() ) 541 fileName += ".pdb"; 542 else 543 fileName += ".xml"; 544 return fileName; 545 } 546 547 private Structure getStructure(AtomCache cache, String name1, String file) 548 { 549 550 PDBFileReader reader = new PDBFileReader(); 551 if ( file != null ){ 552 try { 553 // check if it is a URL: 554 try { 555 URL url = new URL(file); 556 System.out.println(url); 557 558 Structure s = reader.getStructure(url); 559 560 return fixStructureName(s,file); 561 562 } catch ( Exception e){ 563 System.err.println(e.getMessage()); 564 } 565 File f= new File(file); 566 System.out.println("file from local " + f.getAbsolutePath()); 567 Structure s= reader.getStructure(f); 568 return fixStructureName(s, file); 569 } catch (Exception e){ 570 System.err.println("general exception:" + e.getMessage()); 571 System.err.println("unable to load structure from " + file); 572 return null; 573 } 574 } 575 try { 576 Structure s = cache.getStructure(name1); 577 return s; 578 } catch ( Exception e){ 579 System.err.println(e.getMessage()); 580 System.err.println("unable to load structure from dir: " + cache.getPath() + "/"+ name1); 581 return null; 582 } 583 584 } 585 586 /** apply a number of rules to fix the name of the structure if it did not get set during loading. 587 * 588 * @param s 589 * @param file 590 * @return 591 */ 592 private Structure fixStructureName(Structure s, String file) { 593 594 if ( s.getName() != null && (! s.getName().equals(""))) 595 return s; 596 597 s.setName(s.getPDBCode()); 598 599 if ( s.getName() == null || s.getName().equals("")){ 600 File f = new File(file); 601 s.setName(f.getName()); 602 } 603 return s; 604 } 605 606 public String getDbSearchResult(AFPChain afpChain){ 607 return afpChain.toDBSearchResult(); 608 } 609 610 @Override 611 public String printHelp() { 612 StringBuffer buf = new StringBuffer(); 613 StructureAlignment alg = getAlgorithm(); 614 615 buf.append("-------------------").append(newline); 616 buf.append(alg.getAlgorithmName() + " v." + alg.getVersion() + " help: " + newline); 617 buf.append("-------------------").append(newline); 618 buf.append(newline); 619 620 buf.append(alg.getAlgorithmName()).append(" accepts the following parameters:").append(newline); 621 buf.append(newline); 622 623 buf.append("--- pairwise alignments ---").append(newline); 624 buf.append(" two files to align can be specified by providing a path to a file, or a URL:").append(newline); 625 buf.append(" -file1 the first file to align").append(newline); 626 buf.append(" -file2 the second file to align").append(newline); 627 buf.append(" alternatively you can specify PDB files by their PDB ids:").append(newline); 628 buf.append(" -pdbFilePath Path to the directory in your file system that contains the PDB files.").append(newline); 629 buf.append(" -pdb1 PDB ID of target structure. Chain IDs are optional. In order to specify chain IDs write e.g: 5pti.A").append(newline); 630 buf.append(" -pdb2 PDB ID of query structure. Chain IDs are optional. In order to specify chain IDs write e.g: 5pti.A").append(newline); 631 buf.append(newline); 632 633 buf.append(" -h / -help / --help : print this help string.").append(newline); 634 buf.append(" -version: print version info").append(newline); 635 buf.append(" -printXML true/false print the XML representation of the alignment on stdout.").append(newline); 636 buf.append(" -printFatCat true/false print the original FATCAT output to stdout.").append(newline); 637 buf.append(" -printCE true/false print the result in CE style").append(newline); 638 buf.append(" -show3d print a 3D visualisation of the alignment (requires jmolapplet.jar in classpath)").append(newline); 639 buf.append(" -outFile file to write the output to (default: writes XML representation).").append(newline); 640 buf.append(" -outputPDB use this flag together with -outFile to dump the PDB file of the aligned structures, instead of the XML representation, instead of XML").append(newline); 641 buf.append(" -autoFetch true/false if set to true PDB files will automatically get downloaded and stored in the right location. (default: false)").append(newline); 642 buf.append(" -showMenu displays the menu that allows to run alignments through a user interface.").append(newline); 643 buf.append(newline); 644 645 buf.append("--- custom searches ---").append(newline); 646 buf.append(" -alignPairs (mandatory) path to a file that contains a set of pairs to compair").append(newline); 647 buf.append(" -outFile (mandatory) a file that will contain the summary of all the pairwise alignments").append(newline); 648 buf.append(newline); 649 650 ConfigStrucAligParams params = alg.getParameters(); 651 List<String> paramNames = params.getUserConfigParameters(); 652 List<String> paramHelp = params.getUserConfigHelp(); 653 654 assert(paramNames.size() == paramHelp.size()); 655 656 int size = Math.min(paramNames.size(), paramHelp.size()); 657 if(size > 0) { 658 Iterator<String> namesIt = paramNames.iterator(); 659 Iterator<String> helpIt = paramHelp.iterator(); 660 661 buf.append("--- ").append(alg.getAlgorithmName()).append(" parameters: ---").append(newline); 662 Stream.iterate(0, n -> n + 1).limit(size) 663 .map(i -> namesIt.next()) 664 .forEach(name -> buf.append(" -").append(Introspector.decapitalize(name)).append(" ").append(helpIt.next()).append(newline)); 665 } 666 buf.append(newline); 667 668 buf.append(" For boolean arguments: if neither the text >true< or >false< is provided it is assumed to mean >true<. Instead of >-argument false< it is also possible to write -noArgument.").append(newline); 669 buf.append(newline); 670 671 buf.append("--- How to specify what to align ---").append(newline); 672 buf.append(" If only a PDB code is provided, the whole structure will be used for the alignment.").append(newline); 673 buf.append(" To specify a particular chain write as: 4hhb.A (chain IDs are case sensitive, PDB ids are not)").append(newline); 674 buf.append(" To specify that the 1st chain in a structure should be used write: 4hhb:0 .").append(newline); 675 buf.append(" In order to align SCOP domains, provide pdb1/pdb2 as: d4hhba_ Note: if SCOP is not installed at the -pdbFilePath, will automatically download and install.").append(newline); 676 buf.append(newline); 677 678 return buf.toString(); 679 } 680 681}