JAL-629 Firm up bootstrapArgs. Correct '-colour' in test. Separate --headless and...
[jalview.git] / src / jalview / bin / Commands.java
1 package jalview.bin;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Locale;
12 import java.util.Map;
13
14 import jalview.analysis.AlignmentUtils;
15 import jalview.api.AlignmentViewPanel;
16 import jalview.bin.ArgParser.Arg;
17 import jalview.bin.ArgParser.ArgValue;
18 import jalview.bin.ArgParser.ArgValuesMap;
19 import jalview.bin.ArgParser.SubVals;
20 import jalview.datamodel.AlignmentAnnotation;
21 import jalview.datamodel.AlignmentI;
22 import jalview.datamodel.PDBEntry;
23 import jalview.datamodel.SequenceI;
24 import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
25 import jalview.gui.AlignFrame;
26 import jalview.gui.AlignmentPanel;
27 import jalview.gui.AssociatePdbFileWithSeq;
28 import jalview.gui.Desktop;
29 import jalview.gui.Preferences;
30 import jalview.gui.StructureChooser;
31 import jalview.gui.StructureViewer;
32 import jalview.io.AppletFormatAdapter;
33 import jalview.io.DataSourceType;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileLoader;
37 import jalview.io.HtmlSvgOutput;
38 import jalview.io.IdentifyFile;
39 import jalview.schemes.AnnotationColourGradient;
40 import jalview.structure.StructureImportSettings;
41 import jalview.structure.StructureImportSettings.TFType;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.util.HttpUtils;
44 import jalview.util.MessageManager;
45 import jalview.util.Platform;
46 import jalview.ws.dbsources.EBIAlfaFold;
47 import mc_view.PDBChain;
48
49 public class Commands
50 {
51   Desktop desktop;
52
53   private static boolean headless;
54
55   private static ArgParser argParser;
56
57   private Map<String, AlignFrame> afMap;
58
59   public static boolean processArgs(ArgParser ap, boolean h)
60   {
61     argParser = ap;
62     headless = h;
63     boolean argsWereParsed = true;
64     if (headless)
65     {
66       System.setProperty("java.awt.headless", "true");
67     }
68
69     if (argParser != null && argParser.linkedIds() != null)
70     {
71       for (String id : argParser.linkedIds())
72       {
73         Commands cmds = new Commands();
74         if (id == null)
75         {
76           cmds.processUnlinked(id);
77           argsWereParsed &= cmds.wereParsed();
78         }
79         else
80         {
81           cmds.processLinked(id);
82           argsWereParsed &= cmds.wereParsed();
83         }
84         cmds.processImages(id);
85         argsWereParsed &= cmds.wereParsed();
86       }
87
88     }
89     if (argParser.getBool(Arg.QUIT))
90     {
91       Jalview.getInstance().quit();
92       return true;
93     }
94     // carry on with jalview.bin.Jalview
95     return argsWereParsed;
96   }
97
98   boolean argsWereParsed = true; // set false as soon as an arg is found
99
100   private boolean wereParsed()
101   {
102     return argsWereParsed;
103   }
104
105   public Commands()
106   {
107     this(Desktop.instance);
108   }
109
110   public Commands(Desktop d)
111   {
112     this.desktop = d;
113     afMap = new HashMap<String, AlignFrame>();
114   }
115
116   protected void processUnlinked(String id)
117   {
118     processLinked(id);
119   }
120
121   protected void processLinked(String id)
122   {
123     ArgValuesMap avm = new ArgValuesMap(argParser.linkedArgs(id));
124     if (avm == null)
125       return;
126     else
127       argsWereParsed = false;
128
129     /*
130     // script to execute after all loading is completed one way or another
131     String groovyscript = m.get(Arg.GROOVY) == null ? null
132             : m.get(Arg.GROOVY).getValue();
133     String file = m.get(Arg.OPEN) == null ? null
134             : m.get(Arg.OPEN).getValue();
135     String data = null;
136     FileFormatI format = null;
137     DataSourceType protocol = null;
138     */
139     if (avm.hasValue(Arg.OPEN))
140     {
141       long progress = -1;
142
143       boolean first = true;
144       AlignFrame af;
145       for (ArgValue av : avm.getArgValueList(Arg.OPEN))
146       {
147         String openFile = av.getValue();
148         if (openFile == null)
149           continue;
150
151         argsWereParsed = true;
152         if (first)
153         {
154           first = false;
155           if (!headless && desktop != null)
156           {
157             desktop.setProgressBar(
158                     MessageManager.getString(
159                             "status.processing_commandline_args"),
160                     progress = System.currentTimeMillis());
161           }
162         }
163
164         if (!Platform.isJS())
165         /**
166          * ignore in JavaScript -- can't just file existence - could load it?
167          * 
168          * @j2sIgnore
169          */
170         {
171           if (!HttpUtils.startsWithHttpOrHttps(openFile))
172           {
173             if (!(new File(openFile)).exists())
174             {
175               Console.warn("Can't find file '" + openFile + "'");
176             }
177           }
178         }
179
180         DataSourceType protocol = AppletFormatAdapter
181                 .checkProtocol(openFile);
182
183         FileFormatI format = null;
184         try
185         {
186           format = new IdentifyFile().identify(openFile, protocol);
187         } catch (FileFormatException e1)
188         {
189           Console.error("Unknown file format for '" + openFile + "'");
190         }
191
192         af = afMap.get(id);
193         if (af == null)
194         {
195           /*
196            * this approach isn't working yet
197           // get default annotations before opening AlignFrame
198           if (m.get(Arg.SSANNOTATION) != null)
199           {
200             Console.debug("***** SSANNOTATION="
201                     + m.get(Arg.SSANNOTATION).getBoolean());
202           }
203           if (m.get(Arg.NOTEMPFAC) != null)
204           {
205             Console.debug(
206                     "***** NOTEMPFAC=" + m.get(Arg.NOTEMPFAC).getBoolean());
207           }
208           boolean showSecondaryStructure = (m.get(Arg.SSANNOTATION) != null)
209                   ? m.get(Arg.SSANNOTATION).getBoolean()
210                   : false;
211           boolean showTemperatureFactor = (m.get(Arg.NOTEMPFAC) != null)
212                   ? !m.get(Arg.NOTEMPFAC).getBoolean()
213                   : false;
214           Console.debug("***** tempfac=" + showTemperatureFactor
215                   + ", showSS=" + showSecondaryStructure);
216           StructureSelectionManager ssm = StructureSelectionManager
217                   .getStructureSelectionManager(Desktop.instance);
218           if (ssm != null)
219           {
220             ssm.setAddTempFacAnnot(showTemperatureFactor);
221             ssm.setProcessSecondaryStructure(showSecondaryStructure);
222           }
223            */
224
225           // get kind of temperature factor annotation
226           StructureImportSettings.TFType tempfacType = TFType.DEFAULT;
227           if ((!avm.getBoolean(Arg.NOTEMPFAC)) && avm.hasValue(Arg.TEMPFAC))
228           {
229             try
230             {
231               tempfacType = StructureImportSettings.TFType
232                       .valueOf(avm.getArgValue(Arg.TEMPFAC).getValue()
233                               .toUpperCase(Locale.ROOT));
234               Console.debug("Obtained Temperature Factor type of '"
235                       + tempfacType + "'");
236             } catch (IllegalArgumentException e)
237             {
238               // Just an error message!
239               StringBuilder sb = new StringBuilder().append("Cannot set --")
240                       .append(Arg.TEMPFAC.getName()).append(" to '")
241                       .append(tempfacType)
242                       .append("', ignoring.  Valid values are: ");
243               Iterator<StructureImportSettings.TFType> it = Arrays
244                       .stream(StructureImportSettings.TFType.values())
245                       .iterator();
246               while (it.hasNext())
247               {
248                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
249                 if (it.hasNext())
250                   sb.append(", ");
251               }
252               Console.warn(sb.toString());
253             }
254           }
255
256           Console.debug(
257                   "Opening '" + openFile + "' in new alignment frame");
258           FileLoader fileLoader = new FileLoader(!headless);
259
260           StructureImportSettings.setTemperatureFactorType(tempfacType);
261
262           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
263                   format);
264
265           // wrap alignment?
266           if (avm.getBoolean(Arg.WRAP))
267           {
268             af.getCurrentView().setWrapAlignment(true);
269           }
270
271           // colour aligment?
272           if (avm.hasValue(Arg.COLOUR))
273           {
274             af.changeColour_actionPerformed(avm.getValue(Arg.COLOUR));
275           }
276
277           // change alignment frame title
278           if (avm.hasValue(Arg.TITLE))
279             af.setTitle(avm.getValue(Arg.TITLE));
280
281           /* hacky approach to hiding the annotations */
282           // show secondary structure annotations?
283           if (avm.getBoolean(Arg.SSANNOTATION))
284           {
285             // do this better (annotation types?)
286             AlignmentUtils.showOrHideSequenceAnnotations(
287                     af.getCurrentView().getAlignment(),
288                     Collections.singleton("Secondary Structure"), null,
289                     false, false);
290           }
291
292           // show temperature factor annotations?
293           if (avm.getBoolean(Arg.NOTEMPFAC))
294           {
295             // do this better (annotation types?)
296             List<String> hideThese = new ArrayList<>();
297             hideThese.add("Temperature Factor");
298             hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
299             AlignmentUtils.showOrHideSequenceAnnotations(
300                     af.getCurrentView().getAlignment(), hideThese, null,
301                     false, false);
302           }
303           else
304           /* comment out hacky approach up to here and add this line:
305            if (showTemperatureFactor)
306              */
307           {
308             if (avm.hasValue(Arg.TEMPFAC_LABEL))
309             {
310               AlignmentAnnotation aa = AlignmentUtils
311                       .getFirstSequenceAnnotationOfType(
312                               af.getCurrentView().getAlignment(),
313                               AlignmentAnnotation.LINE_GRAPH);
314               String label = avm.getValue(Arg.TEMPFAC_LABEL);
315               if (aa != null)
316               {
317                 aa.label = label;
318               }
319               else
320               {
321                 Console.info(
322                         "Could not find annotation to apply tempfac_label '"
323                                 + label);
324               }
325             }
326           }
327
328           // store the AlignFrame for this id
329           afMap.put(id, af);
330
331           // is it its own structure file?
332           if (format.isStructureFile())
333           {
334             StructureSelectionManager ssm = StructureSelectionManager
335                     .getStructureSelectionManager(Desktop.instance);
336             SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
337             ssm.computeMapping(false, new SequenceI[] { seq }, null,
338                     openFile, DataSourceType.FILE, null);
339           }
340         }
341         else
342         {
343           Console.debug(
344                   "Opening '" + openFile + "' in existing alignment frame");
345           af.getCurrentView().addFile(new File(openFile), format);
346         }
347
348         Console.debug("Command " + Arg.OPEN + " executed successfully!");
349
350       }
351       if (first) // first=true means nothing opened
352       {
353         if (headless)
354         {
355           Console.error("Could not open any files in headless mode");
356           System.exit(1);
357         }
358         else
359         {
360           Console.warn("No more files to open");
361           if (desktop != null)
362             desktop.setProgressBar(null, progress);
363         }
364       }
365
366     }
367
368     // open the structure (from same PDB file or given PDBfile)
369     if (!avm.getBoolean(Arg.NOSTRUCTURE))
370     {
371       AlignFrame af = afMap.get(id);
372       if (avm.hasValue(Arg.STRUCTURE))
373       {
374         for (ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
375         {
376           String val = av.getValue();
377           SubVals subId = new SubVals(val);
378           SequenceI seq = getSpecifiedSequence(af, subId);
379           if (seq == null)
380           {
381             Console.warn("Could not find sequence for argument --"
382                     + Arg.STRUCTURE + "=" + val);
383             // you probably want to continue here, not break
384             // break;
385             continue;
386           }
387           File structureFile = null;
388           if (subId.getContent() != null
389                   && subId.getContent().length() != 0)
390           {
391             structureFile = new File(subId.getContent());
392             Console.debug("Using structure file (from argument) '"
393                     + structureFile.getAbsolutePath() + "'");
394           }
395
396           // TRY THIS
397           /*
398            PDBEntry fileEntry = new AssociatePdbFileWithSeq()
399                   .associatePdbWithSeq(selectedPdbFileName,
400                           DataSourceType.FILE, selectedSequence, true,
401                           Desktop.instance);
402                           
403            sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry },
404                   ap, new SequenceI[]
405                   { selectedSequence });
406           
407            */
408
409           /* THIS DOESN'T WORK */
410           else if (seq.getAllPDBEntries() != null
411                   && seq.getAllPDBEntries().size() > 0)
412           {
413             structureFile = new File(
414                     seq.getAllPDBEntries().elementAt(0).getFile());
415             Console.debug("Using structure file (from sequence) '"
416                     + structureFile.getAbsolutePath() + "'");
417           }
418
419           if (structureFile == null)
420           {
421             Console.warn("Not provided structure file with '" + val + "'");
422             continue;
423           }
424
425           if (!structureFile.exists())
426           {
427             Console.warn("Structure file '"
428                     + structureFile.getAbsoluteFile() + "' not found.");
429             continue;
430           }
431
432           Console.debug("Using structure file "
433                   + structureFile.getAbsolutePath());
434
435           PDBEntry fileEntry = new AssociatePdbFileWithSeq()
436                   .associatePdbWithSeq(structureFile.getAbsolutePath(),
437                           DataSourceType.FILE, seq, true, Desktop.instance);
438
439           // open structure view
440           AlignmentPanel ap = af.alignPanel;
441           if (headless)
442           {
443             Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
444                     StructureViewer.ViewerType.JMOL.toString());
445           }
446           StructureChooser.openStructureFileForSequence(ap, seq,
447                   structureFile);
448         }
449       }
450     }
451
452     // load a pAE file if given
453     if (avm.hasValue(Arg.PAEMATRIX))
454     {
455       AlignFrame af = afMap.get(id);
456       if (af != null)
457       {
458         for (ArgValue av : avm.getArgValueList(Arg.PAEMATRIX))
459         {
460           String val = av.getValue();
461           SubVals subVals = ArgParser.getSubVals(val);
462           File paeFile = new File(subVals.getContent());
463           String paePath = null;
464           try
465           {
466             paePath = paeFile.getCanonicalPath();
467           } catch (IOException e)
468           {
469             paePath = paeFile.getAbsolutePath();
470             Console.warn(
471                     "Problem with the PAE file path: '" + paePath + "'");
472           }
473           String structId = subVals.get("structid");
474           if (subVals.notSet())
475           {
476             // take structid from pdbfilename
477           }
478           if (subVals.has("structfile"))
479           {
480             Console.info("***** Attaching paeFile '" + paePath + "' to "
481                     + "structfile=" + subVals.get("structfile"));
482             EBIAlfaFold.addAlphaFoldPAEToStructure(
483                     af.getCurrentView().getAlignment(), paeFile,
484                     subVals.getIndex(), subVals.get("structfile"), false);
485           }
486           else if (subVals.has("structid"))
487           {
488             Console.info("***** Attaching paeFile '" + paePath + "' to "
489                     + "structid=" + subVals.get("structid"));
490             EBIAlfaFold.addAlphaFoldPAEToStructure(
491                     af.getCurrentView().getAlignment(), paeFile,
492                     subVals.getIndex(), subVals.get("structid"), true);
493           }
494           else
495           {
496             Console.debug("***** Attaching paeFile '" + paePath
497                     + "' to sequence index " + subVals.getIndex());
498             EBIAlfaFold.addAlphaFoldPAEToSequence(
499                     af.getCurrentView().getAlignment(), paeFile,
500                     subVals.getIndex(), null);
501             // required to readjust the height and position of the pAE
502             // annotation
503           }
504           for (AlignmentViewPanel ap : af.getAlignPanels())
505           {
506             ap.adjustAnnotationHeight();
507           }
508         }
509       }
510     }
511
512     boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
513     if (doShading)
514     {
515       AlignFrame af = afMap.get(id);
516       for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
517               .findAnnotation(PDBChain.class.getName().toString()))
518       {
519         AnnotationColourGradient acg = new AnnotationColourGradient(aa,
520                 af.alignPanel.av.getGlobalColourScheme(), 0);
521         acg.setSeqAssociated(true);
522         af.changeColour(acg);
523         Console.info("Changed colour " + acg.toString());
524       }
525     }
526   }
527
528   protected void processImages(String id)
529   {
530     ArgValuesMap avm = new ArgValuesMap(argParser.linkedArgs(id));
531     AlignFrame af = afMap.get(id);
532
533     if (af == null)
534     {
535       Console.warn("Did not have an alignment window for id=" + id);
536       return;
537     }
538
539     if (avm.hasValue(Arg.IMAGE))
540     {
541       for (ArgValue av : avm.getArgValueList(Arg.IMAGE))
542       {
543         String val = av.getValue();
544         SubVals subVal = new SubVals(val);
545         String type = "png"; // default
546         String fileName = subVal.getContent();
547         File file = new File(fileName);
548         if (subVal.has("type"))
549         {
550           type = subVal.get("type");
551         }
552         else if (fileName != null)
553         {
554           for (String ext : new String[] { "svg", "png", "html" })
555           {
556             if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
557             {
558               type = ext;
559             }
560           }
561         }
562         // for moment we disable JSON export
563         Cache.setPropsAreReadOnly(true);
564         Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
565
566         switch (type)
567         {
568         case "svg":
569           Console.debug("Outputting type '" + type + "' to " + fileName);
570           af.createSVG(file);
571           break;
572         case "png":
573           Console.debug("Outputting type '" + type + "' to " + fileName);
574           af.createPNG(file);
575           break;
576         case "html":
577           Console.debug("Outputting type '" + type + "' to " + fileName);
578           HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
579           htmlSVG.exportHTML(fileName);
580           break;
581         default:
582           Console.warn("--image type '" + type + "' not known. Ignoring");
583           break;
584         }
585       }
586     }
587   }
588
589   private SequenceI getSpecifiedSequence(AlignFrame af, SubVals subId)
590   {
591     AlignmentI al = af.getCurrentView().getAlignment();
592     if (-1 < subId.getIndex()
593             && subId.getIndex() < al.getSequences().size())
594     {
595       return al.getSequenceAt(subId.getIndex());
596     }
597     else if (subId.has("seqid"))
598     {
599       return al.findName(subId.get("seqid"));
600     }
601     return null;
602   }
603 }