JAL-629 Add image output and quit
[jalview.git] / src / jalview / bin / Commands.java
1 package jalview.bin;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Map;
12
13 import jalview.analysis.AlignmentUtils;
14 import jalview.api.AlignmentViewPanel;
15 import jalview.bin.ArgParser.Arg;
16 import jalview.bin.ArgParser.ArgValues;
17 import jalview.bin.ArgParser.SubVal;
18 import jalview.datamodel.AlignmentAnnotation;
19 import jalview.datamodel.SequenceI;
20 import jalview.gui.AlignFrame;
21 import jalview.gui.AlignmentPanel;
22 import jalview.gui.Desktop;
23 import jalview.gui.StructureChooser;
24 import jalview.io.AppletFormatAdapter;
25 import jalview.io.DataSourceType;
26 import jalview.io.FileFormatException;
27 import jalview.io.FileFormatI;
28 import jalview.io.FileLoader;
29 import jalview.io.HtmlSvgOutput;
30 import jalview.io.IdentifyFile;
31 import jalview.util.HttpUtils;
32 import jalview.util.MessageManager;
33 import jalview.util.Platform;
34 import jalview.ws.dbsources.EBIAlfaFold;
35
36 public class Commands
37 {
38   Desktop desktop;
39
40   private static boolean headless;
41
42   private static ArgParser argParser;
43
44   private Map<String, AlignFrame> afMap;
45
46   public static void processArgs(ArgParser ap, boolean h)
47   {
48     argParser = ap;
49     headless = h;
50     if (argParser != null && argParser.linkedIds() != null)
51     {
52       for (String id : argParser.linkedIds())
53       {
54         Commands cmds = new Commands();
55         if (id == null)
56         {
57           cmds.processUnlinked(id);
58         }
59         else
60         {
61           cmds.processLinked(id);
62         }
63         cmds.processImages(id);
64       }
65
66       if (argParser.getBool(Arg.QUIT))
67       {
68         Jalview.getInstance().quit();
69       }
70     }
71   }
72
73   public Commands()
74   {
75     this(Desktop.instance);
76   }
77
78   public Commands(Desktop d)
79   {
80     this.desktop = d;
81     afMap = new HashMap<String, AlignFrame>();
82   }
83
84   protected void processUnlinked(String id)
85   {
86     processLinked(id);
87   }
88
89   protected void processLinked(String id)
90   {
91     Map<Arg, ArgValues> m = argParser.linkedArgs(id);
92
93     /*
94     // script to execute after all loading is completed one way or another
95     String groovyscript = m.get(Arg.GROOVY) == null ? null
96             : m.get(Arg.GROOVY).getValue();
97     String file = m.get(Arg.OPEN) == null ? null
98             : m.get(Arg.OPEN).getValue();
99     String data = null;
100     FileFormatI format = null;
101     DataSourceType protocol = null;
102     */
103
104     if (ArgParser.getArgValues(m, Arg.OPEN) != null)
105     {
106       long progress = -1;
107
108       boolean first = true;
109       AlignFrame af;
110       OPEN: for (String openFile : ArgParser.getValues(m, Arg.OPEN))
111       {
112         if (openFile == null)
113           continue OPEN;
114
115         if (first)
116         {
117           first = false;
118           if (!headless)
119           {
120             desktop.setProgressBar(
121                     MessageManager.getString(
122                             "status.processing_commandline_args"),
123                     progress = System.currentTimeMillis());
124           }
125         }
126
127         if (!Platform.isJS())
128         /**
129          * ignore in JavaScript -- can't just file existence - could load it?
130          * 
131          * @j2sIgnore
132          */
133         {
134           if (!HttpUtils.startsWithHttpOrHttps(openFile))
135           {
136             if (!(new File(openFile)).exists())
137             {
138               Console.warn("Can't find file '" + openFile + "'");
139               continue OPEN;
140             }
141           }
142         }
143
144         DataSourceType protocol = AppletFormatAdapter
145                 .checkProtocol(openFile);
146
147         FileFormatI format = null;
148         try
149         {
150           format = new IdentifyFile().identify(openFile, protocol);
151         } catch (FileFormatException e1)
152         {
153           Console.error("Unknown file format for '" + openFile + "'");
154         }
155
156         af = afMap.get(id);
157         if (af == null)
158         {
159           /*
160            * this approach isn't working yet
161           // get default annotations before opening AlignFrame
162           if (m.get(Arg.SSANNOTATION) != null)
163           {
164             Console.debug("***** SSANNOTATION="
165                     + m.get(Arg.SSANNOTATION).getBoolean());
166           }
167           if (m.get(Arg.NOTEMPFAC) != null)
168           {
169             Console.debug(
170                     "***** NOTEMPFAC=" + m.get(Arg.NOTEMPFAC).getBoolean());
171           }
172           boolean showSecondaryStructure = (m.get(Arg.SSANNOTATION) != null)
173                   ? m.get(Arg.SSANNOTATION).getBoolean()
174                   : false;
175           boolean showTemperatureFactor = (m.get(Arg.NOTEMPFAC) != null)
176                   ? !m.get(Arg.NOTEMPFAC).getBoolean()
177                   : false;
178           Console.debug("***** tempfac=" + showTemperatureFactor
179                   + ", showSS=" + showSecondaryStructure);
180           StructureSelectionManager ssm = StructureSelectionManager
181                   .getStructureSelectionManager(Desktop.instance);
182           if (ssm != null)
183           {
184             ssm.setAddTempFacAnnot(showTemperatureFactor);
185             ssm.setProcessSecondaryStructure(showSecondaryStructure);
186           }
187            */
188
189           // get kind of temperature factor annotation
190           AlignmentAnnotation.TFType tempfacType = null;
191           if ((!ArgParser.getBoolean(m, Arg.NOTEMPFAC))
192                   && ArgParser.getArgValues(m, Arg.TEMPFAC) != null)
193           {
194             try
195             {
196               tempfacType = AlignmentAnnotation.TFType.valueOf(ArgParser
197                       .getValue(m, Arg.TEMPFAC).toUpperCase(Locale.ROOT));
198               Console.debug("Obtained Temperature Factor type of '"
199                       + tempfacType + "'");
200             } catch (IllegalArgumentException e)
201             {
202               // Just an error message!
203               StringBuilder sb = new StringBuilder().append("Cannot set --")
204                       .append(Arg.TEMPFAC.getName()).append(" to '")
205                       .append(tempfacType)
206                       .append("', ignoring.  Valid values are: ");
207               Iterator<AlignmentAnnotation.TFType> it = Arrays
208                       .stream(AlignmentAnnotation.TFType.values())
209                       .iterator();
210               while (it.hasNext())
211               {
212                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
213                 if (it.hasNext())
214                   sb.append(", ");
215               }
216               Console.warn(sb.toString());
217             }
218           }
219
220           Console.debug(
221                   "Opening '" + openFile + "' in new alignment frame");
222           FileLoader fileLoader = new FileLoader(!headless);
223           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol, format,
224                   tempfacType);
225
226           // wrap alignment?
227           if (ArgParser.getBoolean(m, Arg.WRAP))
228           {
229             af.getCurrentView().setWrapAlignment(true);
230           }
231
232           // change alignment frame title
233           if (ArgParser.getValue(m, Arg.TITLE) != null)
234             af.setTitle(ArgParser.getValue(m, Arg.TITLE));
235
236           /* hacky approach to hiding the annotations */
237           // show secondary structure annotations?
238           if (ArgParser.getBoolean(m, Arg.SSANNOTATION))
239           {
240             // do this better (annotation types?)
241             AlignmentUtils.showOrHideSequenceAnnotations(
242                     af.getCurrentView().getAlignment(),
243                     Collections.singleton("Secondary Structure"), null,
244                     false, false);
245           }
246
247           // show temperature factor annotations?
248           if (ArgParser.getBoolean(m, Arg.NOTEMPFAC))
249           {
250             // do this better (annotation types?)
251             List<String> hideThese = new ArrayList<>();
252             hideThese.add("Temperature Factor");
253             hideThese.add(MessageManager
254                     .getString("label.alphafold_reliability"));
255             AlignmentUtils.showOrHideSequenceAnnotations(
256                     af.getCurrentView().getAlignment(), hideThese, null,
257                     false, false);
258           }
259           else
260           /* comment out hacky approach up to here and add this line:
261            if (showTemperatureFactor)
262              */
263           {
264             if (ArgParser.getValue(m, Arg.TEMPFAC_LABEL) != null)
265             {
266               AlignmentAnnotation aa = AlignmentUtils
267                       .getFirstSequenceAnnotationOfType(
268                               af.getCurrentView().getAlignment(),
269                               AlignmentAnnotation.LINE_GRAPH);
270               String label = ArgParser.getValue(m, Arg.TEMPFAC_LABEL);
271               if (aa != null)
272               {
273                 aa.label = label;
274               }
275               else
276               {
277                 Console.info(
278                         "Could not find annotation to apply tempfac_label '"
279                                 + label);
280               }
281             }
282           }
283
284           // store the AlignFrame for this id
285           afMap.put(id, af);
286         }
287         else
288         {
289           Console.debug(
290                   "Opening '" + openFile + "' in existing alignment frame");
291           af.getCurrentView().addFile(new File(openFile), format);
292         }
293
294         System.out
295                 .println("Command " + Arg.OPEN + " executed successfully!");
296
297       }
298       if (first) // first=true means nothing opened
299       {
300         if (headless)
301         {
302           Console.error("Could not open any files in headless mode");
303           System.exit(1);
304         }
305       }
306       else
307       {
308         Console.warn("No more files to open");
309         if (desktop != null)
310           desktop.setProgressBar(null, progress);
311       }
312
313     }
314
315     // load a pAE file if given
316     if (ArgParser.getValues(m, Arg.PAEMATRIX) != null)
317     {
318       AlignFrame af = afMap.get(id);
319       if (af != null)
320       {
321         for (String val : ArgParser.getValues(m, Arg.PAEMATRIX))
322         {
323           SubVal subVal = ArgParser.getSubVal(val);
324           File paeFile = new File(subVal.content);
325           EBIAlfaFold.addAlphaFoldPAE(af.getCurrentView().getAlignment(),
326                   paeFile, subVal.index,
327                   "id".equals(subVal.keyName) ? subVal.keyValue : null);
328           // required to readjust the height and position of the pAE
329           // annotation
330           for (AlignmentViewPanel ap : af.getAlignPanels())
331           {
332             ap.adjustAnnotationHeight();
333           }
334         }
335       }
336     }
337
338     // open the structure (from same PDB file or given PDBfile)
339     if (!ArgParser.getBoolean(m, Arg.NOSTRUCTURE))
340     {
341       AlignFrame af = afMap.get(id);
342       if (ArgParser.getArgValues(m, Arg.STRUCTURE) != null)
343       {
344         STRUCTURE: for (String val : ArgParser.getValues(m, Arg.STRUCTURE))
345         {
346           SubVal subId = new SubVal(val);
347           SequenceI seq = getSpecifiedSequence(af, subId);
348           if (seq == null)
349           {
350             Console.warn("Could not find sequence for argument --"
351                     + Arg.STRUCTURE + "=" + val);
352             break STRUCTURE;
353           }
354           File structureFile = null;
355           if (subId.content != null && subId.content.length() != 0)
356           {
357             structureFile = new File(subId.content);
358             Console.debug("Using structure file (from argument) '"
359                     + structureFile.getAbsolutePath() + "'");
360           }
361           /* THIS DOESN'T WORK */
362           else if (seq.getAllPDBEntries() != null
363                   && seq.getAllPDBEntries().size() > 0)
364           {
365             structureFile = new File(
366                     seq.getAllPDBEntries().elementAt(0).getFile());
367             Console.debug("Using structure file (from sequence) '"
368                     + structureFile.getAbsolutePath() + "'");
369           }
370
371           if (structureFile == null)
372           {
373             Console.warn("Not provided structure file with '" + val + "'");
374             continue STRUCTURE;
375           }
376
377           if (!structureFile.exists())
378           {
379             Console.warn("Structure file '"
380                     + structureFile.getAbsoluteFile() + "' not found.");
381             continue STRUCTURE;
382           }
383
384           Console.debug("Using structure file "
385                   + structureFile.getAbsolutePath());
386
387           // open structure view
388           AlignmentPanel ap = af.alignPanel;
389           StructureChooser.openStructureFileForSequence(ap, seq,
390                   structureFile);
391         }
392       }
393     }
394   }
395
396   protected void processImages(String id)
397   {
398     Map<Arg, ArgValues> m = argParser.linkedArgs(id);
399     AlignFrame af = afMap.get(id);
400
401     if (af == null)
402     {
403       Console.warn("Did not have an alignment window for id=" + id);
404       return;
405     }
406
407     if (ArgParser.getValues(m, Arg.IMAGE) != null)
408     {
409       for (String val : ArgParser.getValues(m, Arg.IMAGE))
410       {
411         SubVal subVal = new SubVal(val);
412         String type = "png"; // default
413         String fileName = subVal.content;
414         File file = new File(fileName);
415         if ("type".equals(subVal.keyName))
416         {
417           type = subVal.keyValue;
418         }
419         else if (fileName != null)
420         {
421           for (String ext : new String[] { "svg", "png", "html" })
422           {
423             if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
424             {
425               type = ext;
426             }
427           }
428         }
429         switch (type)
430         {
431         case "svg":
432           af.createSVG(file);
433           break;
434         case "png":
435           af.createPNG(file);
436           break;
437         case "html":
438           HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
439           htmlSVG.exportHTML(fileName);
440           break;
441         }
442       }
443     }
444   }
445
446   private SequenceI getSpecifiedSequence(AlignFrame af, SubVal subId)
447   {
448     SequenceI seq = null;
449     SequenceI[] sequences = af.getCurrentView().getAlignment()
450             .getSequencesArray();
451     if (-1 < subId.index && subId.index < sequences.length)
452     {
453       seq = sequences[subId.index];
454     }
455     else if ("id".equals(subId.keyName))
456     {
457       for (SequenceI s : sequences)
458       {
459         if (s.getDisplayId(false).equals(subId.keyValue))
460         {
461           seq = s;
462           break;
463         }
464       }
465     }
466     return seq;
467   }
468 }