6d514b0eb578377fba6849521f0e486d19287324
[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.AbstractMap;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Map.Entry;
15
16 import jalview.analysis.AlignmentUtils;
17 import jalview.bin.argparser.Arg;
18 import jalview.bin.argparser.ArgParser;
19 import jalview.bin.argparser.ArgValue;
20 import jalview.bin.argparser.ArgValues;
21 import jalview.bin.argparser.ArgValuesMap;
22 import jalview.bin.argparser.SubVals;
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.SequenceI;
26 import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
27 import jalview.gui.AlignFrame;
28 import jalview.gui.AlignmentPanel;
29 import jalview.gui.Desktop;
30 import jalview.gui.Preferences;
31 import jalview.gui.StructureChooser;
32 import jalview.gui.StructureViewer;
33 import jalview.io.AppletFormatAdapter;
34 import jalview.io.DataSourceType;
35 import jalview.io.FileFormat;
36 import jalview.io.FileFormatException;
37 import jalview.io.FileFormatI;
38 import jalview.io.FileLoader;
39 import jalview.io.HtmlSvgOutput;
40 import jalview.io.IdentifyFile;
41 import jalview.schemes.AnnotationColourGradient;
42 import jalview.structure.StructureImportSettings.TFType;
43 import jalview.structure.StructureSelectionManager;
44 import jalview.util.HttpUtils;
45 import jalview.util.MessageManager;
46 import jalview.util.Platform;
47 import mc_view.PDBChain;
48
49 public class Commands
50 {
51   Desktop desktop;
52
53   private boolean headless;
54
55   private ArgParser argParser;
56
57   private Map<String, AlignFrame> afMap;
58
59   private boolean commandArgsProvided = false;
60
61   private boolean argsWereParsed = false;
62
63   public Commands(ArgParser argparser, boolean headless)
64   {
65     this(Desktop.instance, argparser, headless);
66   }
67
68   public Commands(Desktop d, ArgParser argparser, boolean h)
69   {
70     argParser = argparser;
71     headless = h;
72     desktop = d;
73     afMap = new HashMap<String, AlignFrame>();
74     if (argparser != null)
75     {
76       processArgs(argparser, headless);
77     }
78   }
79
80   private boolean processArgs(ArgParser argparser, boolean h)
81   {
82     argParser = argparser;
83     headless = h;
84     boolean theseArgsWereParsed = false;
85
86     if (argParser != null && argParser.linkedIds() != null)
87     {
88       for (String id : argParser.linkedIds())
89       {
90         ArgValuesMap avm = argParser.linkedArgs(id);
91         theseArgsWereParsed = true;
92         if (id == null)
93         {
94           theseArgsWereParsed &= processUnlinked(id);
95         }
96         else
97         {
98           theseArgsWereParsed &= processLinked(id);
99         }
100         theseArgsWereParsed &= processImages(id);
101
102         // close ap
103         if (avm.getBoolean(Arg.CLOSE))
104         {
105           AlignFrame af = afMap.get(id);
106           if (af != null)
107           {
108             af.closeMenuItem_actionPerformed(true);
109           }
110         }
111
112       }
113
114     }
115     if (argParser.getBool(Arg.QUIT))
116     {
117       Jalview.getInstance().quit();
118       return true;
119     }
120     // carry on with jalview.bin.Jalview
121     argsWereParsed = theseArgsWereParsed;
122     return argsWereParsed;
123   }
124
125   public boolean commandArgsProvided()
126   {
127     return commandArgsProvided;
128   }
129
130   public boolean argsWereParsed()
131   {
132     return argsWereParsed;
133   }
134
135   protected boolean processUnlinked(String id)
136   {
137     return processLinked(id);
138   }
139
140   protected boolean processLinked(String id)
141   {
142     boolean theseArgsWereParsed = false;
143     ArgValuesMap avm = argParser.linkedArgs(id);
144     if (avm == null)
145       return true;
146
147     /*
148      * // script to execute after all loading is completed one way or another String
149      * groovyscript = m.get(Arg.GROOVY) == null ? null :
150      * m.get(Arg.GROOVY).getValue(); String file = m.get(Arg.OPEN) == null ? null :
151      * m.get(Arg.OPEN).getValue(); String data = null; FileFormatI format = null;
152      * DataSourceType protocol = null;
153      */
154     if (avm.containsArg(Arg.OPEN) || avm.containsArg(Arg.OPENNEW))
155     {
156       commandArgsProvided = true;
157       long progress = -1;
158
159       boolean first = true;
160       boolean progressBarSet = false;
161       AlignFrame af;
162       // Combine the OPEN and OPENNEW files into one list, along with whether it
163       // was OPEN or OPENNEW
164       List<Entry<Arg, ArgValue>> openAvList = new ArrayList<>();
165       avm.getArgValueList(Arg.OPEN).stream()
166               .forEachOrdered(av -> openAvList.add(
167                       new AbstractMap.SimpleEntry<Arg, ArgValue>(Arg.OPEN,
168                               av)));
169       avm.getArgValueList(Arg.OPENNEW).stream()
170               .forEachOrdered(av -> openAvList
171                       .add(new AbstractMap.SimpleEntry<Arg, ArgValue>(
172                               Arg.OPENNEW, av)));
173       for (Entry<Arg, ArgValue> aav : openAvList)
174       {
175         Arg a = aav.getKey();
176         ArgValue av = aav.getValue();
177         String openFile = av.getValue();
178         if (openFile == null)
179           continue;
180
181         theseArgsWereParsed = true;
182         if (first)
183         {
184           first = false;
185           if (!headless && desktop != null)
186           {
187             desktop.setProgressBar(
188                     MessageManager.getString(
189                             "status.processing_commandline_args"),
190                     progress = System.currentTimeMillis());
191             progressBarSet = true;
192           }
193         }
194
195         if (!Platform.isJS())
196         /**
197          * ignore in JavaScript -- can't just file existence - could load it?
198          * 
199          * @j2sIgnore
200          */
201         {
202           if (!HttpUtils.startsWithHttpOrHttps(openFile))
203           {
204             if (!(new File(openFile)).exists())
205             {
206               Console.warn("Can't find file '" + openFile + "'");
207             }
208           }
209         }
210
211         DataSourceType protocol = AppletFormatAdapter
212                 .checkProtocol(openFile);
213
214         FileFormatI format = null;
215         try
216         {
217           format = new IdentifyFile().identify(openFile, protocol);
218         } catch (FileFormatException e1)
219         {
220           Console.error("Unknown file format for '" + openFile + "'");
221         }
222
223         af = afMap.get(id);
224         if (af == null || "true".equals(av.getSubVal("new"))
225                 || a == Arg.OPENNEW || format == FileFormat.Jalview)
226         {
227           /*
228            * this approach isn't working yet // get default annotations before opening
229            * AlignFrame if (m.get(Arg.SSANNOTATION) != null) {
230            * Console.debug("##### SSANNOTATION=" + m.get(Arg.SSANNOTATION).getBoolean());
231            * } if (m.get(Arg.NOTEMPFAC) != null) { Console.debug( "##### NOTEMPFAC=" +
232            * m.get(Arg.NOTEMPFAC).getBoolean()); } boolean showSecondaryStructure =
233            * (m.get(Arg.SSANNOTATION) != null) ? m.get(Arg.SSANNOTATION).getBoolean() :
234            * false; boolean showTemperatureFactor = (m.get(Arg.NOTEMPFAC) != null) ?
235            * !m.get(Arg.NOTEMPFAC).getBoolean() : false; Console.debug("##### tempfac=" +
236            * showTemperatureFactor + ", showSS=" + showSecondaryStructure);
237            * StructureSelectionManager ssm = StructureSelectionManager
238            * .getStructureSelectionManager(Desktop.instance); if (ssm != null) {
239            * ssm.setAddTempFacAnnot(showTemperatureFactor);
240            * ssm.setProcessSecondaryStructure(showSecondaryStructure); }
241            */
242
243           Console.debug(
244                   "Opening '" + openFile + "' in new alignment frame");
245           FileLoader fileLoader = new FileLoader(!headless);
246
247           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
248                   format);
249
250           // wrap alignment?
251           if (avm.getBoolean(Arg.WRAP))
252           {
253             af.getCurrentView().setWrapAlignment(true);
254           }
255
256           // colour aligment?
257           if (avm.containsArg(Arg.COLOUR))
258           {
259             af.changeColour_actionPerformed(avm.getValue(Arg.COLOUR));
260           }
261
262           // change alignment frame title
263           if (avm.containsArg(Arg.TITLE))
264             af.setTitle(avm.getValue(Arg.TITLE));
265
266           /* hacky approach to hiding the annotations */
267           // show secondary structure annotations?
268           if (avm.getBoolean(Arg.SSANNOTATION))
269           {
270             // do this better (annotation types?)
271             AlignmentUtils.showOrHideSequenceAnnotations(
272                     af.getCurrentView().getAlignment(),
273                     Collections.singleton("Secondary Structure"), null,
274                     false, false);
275           }
276
277           // show temperature factor annotations?
278           if (avm.getBoolean(Arg.NOTEMPFAC))
279           {
280             // do this better (annotation types?)
281             List<String> hideThese = new ArrayList<>();
282             hideThese.add("Temperature Factor");
283             hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
284             AlignmentUtils.showOrHideSequenceAnnotations(
285                     af.getCurrentView().getAlignment(), hideThese, null,
286                     false, false);
287           }
288           else
289           /*
290            * comment out hacky approach up to here and add this line: if
291            * (showTemperatureFactor)
292            */
293           {
294             if (avm.containsArg(Arg.TEMPFAC_LABEL))
295             {
296               AlignmentAnnotation aa = AlignmentUtils
297                       .getFirstSequenceAnnotationOfType(
298                               af.getCurrentView().getAlignment(),
299                               AlignmentAnnotation.LINE_GRAPH);
300               String label = avm.getValue(Arg.TEMPFAC_LABEL);
301               if (aa != null)
302               {
303                 aa.label = label;
304               }
305               else
306               {
307                 Console.info(
308                         "Could not find annotation to apply tempfac_label '"
309                                 + label);
310               }
311             }
312           }
313
314           // store the AlignFrame for this id
315           afMap.put(id, af);
316
317           // is it its own structure file?
318           if (format.isStructureFile())
319           {
320             StructureSelectionManager ssm = StructureSelectionManager
321                     .getStructureSelectionManager(Desktop.instance);
322             SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
323             ssm.computeMapping(false, new SequenceI[] { seq }, null,
324                     openFile, DataSourceType.FILE, null, null, null, false);
325           }
326         }
327         else
328         {
329           Console.debug(
330                   "Opening '" + openFile + "' in existing alignment frame");
331           af.getCurrentView().addFile(new File(openFile), format, false);
332         }
333
334         Console.debug("Command " + Arg.OPEN + " executed successfully!");
335
336       }
337       if (first) // first=true means nothing opened
338       {
339         if (headless)
340         {
341           Jalview.exit("Could not open any files in headless mode", 1);
342         }
343         else
344         {
345           Console.warn("No more files to open");
346         }
347       }
348       if (progressBarSet && desktop != null)
349         desktop.setProgressBar(null, progress);
350
351     }
352
353     // open the structure (from same PDB file or given PDBfile)
354     if (!avm.getBoolean(Arg.NOSTRUCTURE))
355     {
356       AlignFrame af = afMap.get(id);
357       if (avm.containsArg(Arg.STRUCTURE))
358       {
359         commandArgsProvided = true;
360         for (ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
361         {
362           String val = av.getValue();
363           SubVals subVals = av.getSubVals();
364           SequenceI seq = getSpecifiedSequence(af, subVals);
365           if (seq == null)
366           {
367             // Could not find sequence from subId, let's assume the first
368             // sequence in the alignframe
369             AlignmentI al = af.getCurrentView().getAlignment();
370             seq = al.getSequenceAt(0);
371           }
372
373           if (seq == null)
374           {
375             Console.warn("Could not find sequence for argument "
376                     + Arg.STRUCTURE.argString() + "=" + val);
377             // you probably want to continue here, not break
378             // break;
379             continue;
380           }
381           File structureFile = null;
382           if (subVals.getContent() != null
383                   && subVals.getContent().length() != 0)
384           {
385             structureFile = new File(subVals.getContent());
386             Console.debug("Using structure file (from argument) '"
387                     + structureFile.getAbsolutePath() + "'");
388           }
389           // TRY THIS
390           /*
391            * PDBEntry fileEntry = new AssociatePdbFileWithSeq()
392            * .associatePdbWithSeq(selectedPdbFileName, DataSourceType.FILE,
393            * selectedSequence, true, Desktop.instance);
394            * 
395            * sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap, new
396            * SequenceI[] { selectedSequence });
397            * 
398            */
399           /* THIS DOESN'T WORK */
400           else if (seq.getAllPDBEntries() != null
401                   && seq.getAllPDBEntries().size() > 0)
402           {
403             structureFile = new File(
404                     seq.getAllPDBEntries().elementAt(0).getFile());
405             Console.debug("Using structure file (from sequence) '"
406                     + structureFile.getAbsolutePath() + "'");
407           }
408
409           if (structureFile == null)
410           {
411             Console.warn("Not provided structure file with '" + val + "'");
412             continue;
413           }
414
415           if (!structureFile.exists())
416           {
417             Console.warn("Structure file '"
418                     + structureFile.getAbsoluteFile() + "' not found.");
419             continue;
420           }
421
422           Console.debug("Using structure file "
423                   + structureFile.getAbsolutePath());
424
425           // ##### Does this need to happen? Follow
426           // openStructureFileForSequence() below
427           /*
428           PDBEntry fileEntry = new AssociatePdbFileWithSeq()
429                   .associatePdbWithSeq(structureFile.getAbsolutePath(),
430                           DataSourceType.FILE, seq, true, Desktop.instance);
431                           */
432
433           // open structure view
434           AlignmentPanel ap = af.alignPanel;
435           if (headless)
436           {
437             Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
438                     StructureViewer.ViewerType.JMOL.toString());
439           }
440
441           String structureFilepath = structureFile.getAbsolutePath();
442
443           // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
444           String paeFilepath = subVals.getWithSubstitutions(argParser, id,
445                   "paematrix");
446           String paeLabel = subVals.get("paelabel");
447           ArgValue paeAv = getArgAssociatedWithStructure(Arg.PAEMATRIX, avm,
448                   af, structureFilepath);
449           if (paeFilepath == null && paeAv != null)
450           {
451             SubVals sv = paeAv.getSubVals();
452             File paeFile = new File(sv.getContent());
453
454             paeLabel = sv.get("label");
455             try
456             {
457               paeFilepath = paeFile.getCanonicalPath();
458             } catch (IOException e)
459             {
460               paeFilepath = paeFile.getAbsolutePath();
461               Console.warn("Problem with the PAE file path: '"
462                       + paeFile.getPath() + "'");
463             }
464           }
465
466           // get TEMPFAC type from subvals or Arg.TEMPFAC
467           String tftString = subVals.get("tempfac");
468           TFType tft = avm.getBoolean(Arg.NOTEMPFAC) ? null
469                   : TFType.DEFAULT;
470           ArgValue tftAv = getArgAssociatedWithStructure(Arg.TEMPFAC, avm,
471                   af, structureFilepath);
472           if (tftString == null && tftAv != null)
473           {
474             tftString = tftAv.getSubVals().getContent();
475           }
476           if (tftString != null)
477           {
478             // get kind of temperature factor annotation
479             try
480             {
481               tft = TFType.valueOf(tftString.toUpperCase(Locale.ROOT));
482               Console.debug("Obtained Temperature Factor type of '" + tft
483                       + "' for structure '" + structureFilepath + "'");
484             } catch (IllegalArgumentException e)
485             {
486               // Just an error message!
487               StringBuilder sb = new StringBuilder().append("Cannot set ")
488                       .append(Arg.TEMPFAC.argString()).append(" to '")
489                       .append(tft)
490                       .append("', ignoring.  Valid values are: ");
491               Iterator<TFType> it = Arrays.stream(TFType.values())
492                       .iterator();
493               while (it.hasNext())
494               {
495                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
496                 if (it.hasNext())
497                   sb.append(", ");
498               }
499               Console.warn(sb.toString());
500             }
501           }
502
503           // TODO pass PAE label
504           StructureChooser.openStructureFileForSequence(null, null, ap, seq,
505                   false, structureFilepath, tft, paeFilepath, false);
506         }
507       }
508     }
509
510     boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
511     if (doShading)
512     {
513       AlignFrame af = afMap.get(id);
514       for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
515               .findAnnotation(PDBChain.class.getName().toString()))
516       {
517         AnnotationColourGradient acg = new AnnotationColourGradient(aa,
518                 af.alignPanel.av.getGlobalColourScheme(), 0);
519         acg.setSeqAssociated(true);
520         af.changeColour(acg);
521         Console.info("Changed colour " + acg.toString());
522       }
523     }
524
525     return theseArgsWereParsed;
526   }
527
528   protected boolean processImages(String id)
529   {
530     ArgValuesMap avm = 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 false;
537     }
538
539     if (avm.containsArg(Arg.IMAGE))
540     {
541       for (ArgValue av : avm.getArgValueList(Arg.IMAGE))
542       {
543         String val = av.getValue();
544         SubVals subVal = av.getSubVals();
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(Arg.IMAGE.argString() + " type '" + type
583                   + "' not known. Ignoring");
584           break;
585         }
586       }
587     }
588     return true;
589   }
590
591   private SequenceI getSpecifiedSequence(AlignFrame af, SubVals subId)
592   {
593     if (subId == null)
594       return null;
595     AlignmentI al = af.getCurrentView().getAlignment();
596     if (subId.has("seqid"))
597     {
598       return al.findName(subId.get("seqid"));
599     }
600     else if (-1 < subId.getIndex()
601             && subId.getIndex() < al.getSequences().size())
602     {
603       return al.getSequenceAt(subId.getIndex());
604     }
605     return null;
606   }
607
608   // returns the first Arg value intended for the structure structFilename
609   // (in the given AlignFrame from the ArgValuesMap)
610   private ArgValue getArgAssociatedWithStructure(Arg arg, ArgValuesMap avm,
611           AlignFrame af, String structFilename)
612   {
613     if (af != null)
614     {
615       for (ArgValue av : avm.getArgValueList(arg))
616       {
617         SubVals subVals = av.getSubVals();
618         String structid = subVals.get("structid");
619         String structfile = subVals.get("structfile");
620
621         // let's find a structure
622         if (structfile == null && structid == null)
623         {
624           ArgValue likelyStructure = avm.getClosestPreviousArgValueOfArg(av,
625                   Arg.STRUCTURE);
626           if (likelyStructure != null)
627           {
628             SubVals sv = likelyStructure.getSubVals();
629             if (sv != null && sv.has(ArgValues.ID))
630             {
631               structid = sv.get(ArgValues.ID);
632             }
633             else
634             {
635               structfile = likelyStructure.getValue();
636               Console.debug(
637                       "##### Comparing closest previous structure argument '"
638                               + structfile + "'");
639             }
640           }
641         }
642
643         if (structfile == null && structid != null)
644         {
645           StructureSelectionManager ssm = StructureSelectionManager
646                   .getStructureSelectionManager(Desktop.instance);
647           if (ssm != null)
648           {
649             structfile = ssm.findFileForPDBId(structid);
650           }
651         }
652         if (structfile != null && structfile.equals(structFilename))
653         {
654           return av;
655         }
656       }
657     }
658     return null;
659   }
660 }