e1c88e1df5c9e6694b78b54b3c7cad054b0adbc4
[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.net.URISyntaxException;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.EnumSet;
10 import java.util.HashMap;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Locale;
14 import java.util.Map;
15
16 import jalview.analysis.AlignmentUtils;
17 import jalview.api.structures.JalviewStructureDisplayI;
18 import jalview.bin.argparser.Arg;
19 import jalview.bin.argparser.ArgParser;
20 import jalview.bin.argparser.ArgParser.Position;
21 import jalview.bin.argparser.ArgValue;
22 import jalview.bin.argparser.ArgValuesMap;
23 import jalview.bin.argparser.SubVals;
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.AppJmol;
30 import jalview.gui.Desktop;
31 import jalview.gui.Preferences;
32 import jalview.gui.StructureChooser;
33 import jalview.gui.StructureViewer;
34 import jalview.gui.StructureViewer.ViewerType;
35 import jalview.io.AppletFormatAdapter;
36 import jalview.io.BackupFiles;
37 import jalview.io.BioJsHTMLOutput;
38 import jalview.io.DataSourceType;
39 import jalview.io.FileFormat;
40 import jalview.io.FileFormatException;
41 import jalview.io.FileFormatI;
42 import jalview.io.FileFormats;
43 import jalview.io.FileLoader;
44 import jalview.io.HtmlSvgOutput;
45 import jalview.io.IdentifyFile;
46 import jalview.io.NewickFile;
47 import jalview.io.exceptions.ImageOutputException;
48 import jalview.schemes.ColourSchemeI;
49 import jalview.schemes.ColourSchemeProperty;
50 import jalview.structure.StructureImportSettings.TFType;
51 import jalview.structure.StructureSelectionManager;
52 import jalview.util.FileUtils;
53 import jalview.util.HttpUtils;
54 import jalview.util.ImageMaker;
55 import jalview.util.ImageMaker.TYPE;
56 import jalview.util.MessageManager;
57 import jalview.util.Platform;
58 import jalview.util.imagemaker.BitmapImageSizing;
59
60 public class Commands
61 {
62   Desktop desktop;
63
64   private boolean headless;
65
66   private ArgParser argParser;
67
68   private Map<String, AlignFrame> afMap;
69
70   private boolean commandArgsProvided = false;
71
72   private boolean argsWereParsed = false;
73
74   public Commands(ArgParser argparser, boolean headless)
75   {
76     this(Desktop.instance, argparser, headless);
77   }
78
79   public Commands(Desktop d, ArgParser argparser, boolean h)
80   {
81     argParser = argparser;
82     headless = h;
83     desktop = d;
84     afMap = new HashMap<>();
85     if (argparser != null)
86     {
87       processArgs(argparser, headless);
88     }
89   }
90
91   private boolean processArgs(ArgParser argparser, boolean h)
92   {
93     argParser = argparser;
94     headless = h;
95     boolean theseArgsWereParsed = false;
96
97     if (argParser != null && argParser.getLinkedIds() != null)
98     {
99       for (String id : argParser.getLinkedIds())
100       {
101         ArgValuesMap avm = argParser.getLinkedArgs(id);
102         theseArgsWereParsed = true;
103         theseArgsWereParsed &= processLinked(id);
104         processGroovyScript(id);
105         boolean processLinkedOkay = theseArgsWereParsed;
106
107         // wait around until alignFrame isn't busy
108         AlignFrame af = afMap.get(id);
109         while (af != null && af.getViewport().isCalcInProgress())
110         {
111           try
112           {
113             Thread.sleep(25);
114           } catch (Exception q)
115           {
116           }
117           ;
118         }
119
120         theseArgsWereParsed &= processImages(id);
121         if (processLinkedOkay)
122           theseArgsWereParsed &= processOutput(id);
123
124         // close ap
125         if (avm.getBoolean(Arg.CLOSE))
126         {
127           af = afMap.get(id);
128           if (af != null)
129           {
130             af.closeMenuItem_actionPerformed(true);
131           }
132         }
133
134       }
135
136     }
137     if (argParser.getBoolean(Arg.QUIT))
138     {
139       Jalview.getInstance().quit();
140       return true;
141     }
142     // carry on with jalview.bin.Jalview
143     argsWereParsed = theseArgsWereParsed;
144     return argsWereParsed;
145   }
146
147   public boolean commandArgsProvided()
148   {
149     return commandArgsProvided;
150   }
151
152   public boolean argsWereParsed()
153   {
154     return argsWereParsed;
155   }
156
157   protected boolean processUnlinked(String id)
158   {
159     return processLinked(id);
160   }
161
162   protected boolean processLinked(String id)
163   {
164     boolean theseArgsWereParsed = false;
165     ArgValuesMap avm = argParser.getLinkedArgs(id);
166     if (avm == null)
167       return true;
168
169     // set wrap scope here so it can be applied after structures are opened
170     boolean wrap = false;
171
172     if (avm.containsArg(Arg.APPEND) || avm.containsArg(Arg.OPEN))
173     {
174       commandArgsProvided = true;
175       long progress = -1;
176
177       boolean first = true;
178       boolean progressBarSet = false;
179       AlignFrame af;
180       // Combine the APPEND and OPEN files into one list, along with whether it
181       // was APPEND or OPEN
182       List<ArgValue> openAvList = new ArrayList<>();
183       openAvList.addAll(avm.getArgValueList(Arg.OPEN));
184       openAvList.addAll(avm.getArgValueList(Arg.APPEND));
185       // sort avlist based on av.getArgIndex()
186       Collections.sort(openAvList);
187       for (ArgValue av : openAvList)
188       {
189         Arg a = av.getArg();
190         SubVals sv = av.getSubVals();
191         String openFile = av.getValue();
192         if (openFile == null)
193           continue;
194
195         theseArgsWereParsed = true;
196         if (first)
197         {
198           first = false;
199           if (!headless && desktop != null)
200           {
201             desktop.setProgressBar(
202                     MessageManager.getString(
203                             "status.processing_commandline_args"),
204                     progress = System.currentTimeMillis());
205             progressBarSet = true;
206           }
207         }
208
209         if (!Platform.isJS())
210         /**
211          * ignore in JavaScript -- can't just file existence - could load it?
212          * 
213          * @j2sIgnore
214          */
215         {
216           if (!HttpUtils.startsWithHttpOrHttps(openFile))
217           {
218             if (!(new File(openFile)).exists())
219             {
220               Console.warn("Can't find file '" + openFile + "'");
221             }
222           }
223         }
224
225         DataSourceType protocol = AppletFormatAdapter
226                 .checkProtocol(openFile);
227
228         FileFormatI format = null;
229         try
230         {
231           format = new IdentifyFile().identify(openFile, protocol);
232         } catch (FileFormatException e1)
233         {
234           Console.error("Unknown file format for '" + openFile + "'");
235         }
236
237         af = afMap.get(id);
238         // When to open a new AlignFrame
239         if (af == null || "true".equals(av.getSubVal("new"))
240                 || a == Arg.OPEN || format == FileFormat.Jalview)
241         {
242           if (a == Arg.OPEN)
243           {
244             Jalview.testoutput(argParser, Arg.OPEN, "examples/uniref50.fa",
245                     openFile);
246           }
247
248           Console.debug(
249                   "Opening '" + openFile + "' in new alignment frame");
250           FileLoader fileLoader = new FileLoader(!headless);
251
252           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
253                   format);
254
255           // colour alignment?
256           String colour = ArgParser.getFromSubValArgOrPref(avm, av,
257                   Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", "");
258           if ("" != colour)
259           {
260             ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
261                     af.getViewport(), af.getViewport().getAlignment(),
262                     colour);
263
264             if (cs == null && !"None".equals(colour))
265             {
266               Console.warn(
267                       "Couldn't parse '" + colour + "' as a colourscheme.");
268             }
269             else
270             {
271               af.changeColour(cs);
272             }
273             Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
274           }
275
276           // Change alignment frame title
277           String title = ArgParser.getFromSubValArgOrPref(avm, av,
278                   Arg.TITLE, sv, null, null, null);
279           if (title != null)
280           {
281             af.setTitle(title);
282             Jalview.testoutput(argParser, Arg.TITLE, "test title", title);
283           }
284
285           // Add features
286           String featuresfile = ArgParser.getValueFromSubValOrArg(avm, av,
287                   Arg.FEATURES, sv);
288           if (featuresfile != null)
289           {
290             af.parseFeaturesFile(featuresfile,
291                     AppletFormatAdapter.checkProtocol(featuresfile));
292             Jalview.testoutput(argParser, Arg.FEATURES,
293                     "examples/testdata/plantfdx.features", featuresfile);
294           }
295
296           // Add annotations from file
297           String annotationsfile = ArgParser.getValueFromSubValOrArg(avm,
298                   av, Arg.ANNOTATIONS, sv);
299           if (annotationsfile != null)
300           {
301             af.loadJalviewDataFile(annotationsfile, null, null, null);
302             Jalview.testoutput(argParser, Arg.ANNOTATIONS,
303                     "examples/testdata/plantfdx.annotations",
304                     annotationsfile);
305           }
306
307           // Set or clear the sortbytree flag
308           boolean sortbytree = ArgParser.getBoolFromSubValOrArg(avm,
309                   Arg.SORTBYTREE, sv);
310           if (sortbytree)
311           {
312             af.getViewport().setSortByTree(true);
313             Jalview.testoutput(argParser, Arg.SORTBYTREE);
314           }
315
316           // Load tree from file
317           String treefile = ArgParser.getValueFromSubValOrArg(avm, av,
318                   Arg.TREE, sv);
319           if (treefile != null)
320           {
321             try
322             {
323               NewickFile nf = new NewickFile(treefile,
324                       AppletFormatAdapter.checkProtocol(treefile));
325               af.getViewport().setCurrentTree(
326                       af.showNewickTree(nf, treefile).getTree());
327               Jalview.testoutput(argParser, Arg.TREE,
328                       "examples/testdata/uniref50_test_tree", treefile);
329             } catch (IOException e)
330             {
331               Console.warn("Couldn't add tree " + treefile, e);
332             }
333           }
334
335           // Show secondary structure annotations?
336           boolean showSSAnnotations = ArgParser.getFromSubValArgOrPref(avm,
337                   Arg.SHOWSSANNOTATIONS, av.getSubVals(), null,
338                   "STRUCT_FROM_PDB", true);
339           af.setAnnotationsVisibility(showSSAnnotations, true, false);
340
341           // Show sequence annotations?
342           boolean showAnnotations = ArgParser.getFromSubValArgOrPref(avm,
343                   Arg.SHOWANNOTATIONS, av.getSubVals(), null,
344                   "SHOW_ANNOTATIONS", true);
345           af.setAnnotationsVisibility(showAnnotations, false, true);
346
347           // show temperature factor annotations?
348           if (avm.getBoolean(Arg.NOTEMPFAC))
349           {
350             // do this better (annotation types?)
351             List<String> hideThese = new ArrayList<>();
352             hideThese.add("Temperature Factor");
353             hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
354             AlignmentUtils.showOrHideSequenceAnnotations(
355                     af.getCurrentView().getAlignment(), hideThese, null,
356                     false, false);
357           }
358
359           // wrap alignment? do this last for formatting reasons
360           wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv, null,
361                   "WRAP_ALIGNMENT", false);
362           // af.setWrapFormat(wrap) is applied after structures are opened for
363           // annotation reasons
364
365           // store the AlignFrame for this id
366           afMap.put(id, af);
367
368           // is it its own structure file?
369           if (format.isStructureFile())
370           {
371             StructureSelectionManager ssm = StructureSelectionManager
372                     .getStructureSelectionManager(Desktop.instance);
373             SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
374             ssm.computeMapping(false, new SequenceI[] { seq }, null,
375                     openFile, DataSourceType.FILE, null, null, null, false);
376           }
377         }
378         else
379         {
380           Console.debug(
381                   "Opening '" + openFile + "' in existing alignment frame");
382           DataSourceType dst = HttpUtils.startsWithHttpOrHttps(openFile)
383                   ? DataSourceType.URL
384                   : DataSourceType.FILE;
385           FileLoader fileLoader = new FileLoader(!headless);
386           fileLoader.LoadFile(af.getCurrentView(), openFile, dst, null,
387                   false);
388         }
389
390         Console.debug("Command " + Arg.APPEND + " executed successfully!");
391
392       }
393       if (first) // first=true means nothing opened
394       {
395         if (headless)
396         {
397           Jalview.exit("Could not open any files in headless mode", 1);
398         }
399         else
400         {
401           Console.warn("No more files to open");
402         }
403       }
404       if (progressBarSet && desktop != null)
405         desktop.setProgressBar(null, progress);
406
407     }
408
409     // open the structure (from same PDB file or given PDBfile)
410     if (!avm.getBoolean(Arg.NOSTRUCTURE))
411     {
412       AlignFrame af = afMap.get(id);
413       if (avm.containsArg(Arg.STRUCTURE))
414       {
415         commandArgsProvided = true;
416         for (ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
417         {
418           String val = av.getValue();
419           SubVals subVals = av.getSubVals();
420           SequenceI seq = getSpecifiedSequence(af, avm, av);
421           if (seq == null)
422           {
423             // Could not find sequence from subId, let's assume the first
424             // sequence in the alignframe
425             AlignmentI al = af.getCurrentView().getAlignment();
426             seq = al.getSequenceAt(0);
427           }
428
429           if (seq == null)
430           {
431             Console.warn("Could not find sequence for argument "
432                     + Arg.STRUCTURE.argString() + "=" + val);
433             // you probably want to continue here, not break
434             // break;
435             continue;
436           }
437           File structureFile = null;
438           if (subVals.getContent() != null
439                   && subVals.getContent().length() != 0)
440           {
441             structureFile = new File(subVals.getContent());
442             Console.debug("Using structure file (from argument) '"
443                     + structureFile.getAbsolutePath() + "'");
444           }
445           // TRY THIS
446           /*
447            * PDBEntry fileEntry = new AssociatePdbFileWithSeq()
448            * .associatePdbWithSeq(selectedPdbFileName, DataSourceType.FILE,
449            * selectedSequence, true, Desktop.instance);
450            * 
451            * sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap, new
452            * SequenceI[] { selectedSequence });
453            * 
454            */
455           /* THIS DOESN'T WORK */
456           else if (seq.getAllPDBEntries() != null
457                   && seq.getAllPDBEntries().size() > 0)
458           {
459             structureFile = new File(
460                     seq.getAllPDBEntries().elementAt(0).getFile());
461             Console.debug("Using structure file (from sequence) '"
462                     + structureFile.getAbsolutePath() + "'");
463           }
464
465           if (structureFile == null)
466           {
467             Console.warn("Not provided structure file with '" + val + "'");
468             continue;
469           }
470
471           if (!structureFile.exists())
472           {
473             Console.warn("Structure file '"
474                     + structureFile.getAbsoluteFile() + "' not found.");
475             continue;
476           }
477
478           Console.debug("Using structure file "
479                   + structureFile.getAbsolutePath());
480
481           // open structure view
482           AlignmentPanel ap = af.alignPanel;
483           if (headless)
484           {
485             Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
486                     StructureViewer.ViewerType.JMOL.toString());
487           }
488
489           String structureFilepath = structureFile.getAbsolutePath();
490
491           // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
492           String paeFilepath = ArgParser
493                   .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
494                           Arg.PAEMATRIX, Position.AFTER, av, subVals, null,
495                           null, null);
496           if (paeFilepath != null)
497           {
498             File paeFile = new File(paeFilepath);
499
500             try
501             {
502               paeFilepath = paeFile.getCanonicalPath();
503             } catch (IOException e)
504             {
505               paeFilepath = paeFile.getAbsolutePath();
506               Console.warn("Problem with the PAE file path: '"
507                       + paeFile.getPath() + "'");
508             }
509           }
510
511           // showing annotations from structure file or not
512           boolean ssFromStructure = ArgParser.getFromSubValArgOrPref(avm,
513                   Arg.SHOWSSANNOTATIONS, subVals, null, "STRUCT_FROM_PDB",
514                   true);
515
516           // get TEMPFAC type from subvals or Arg.TEMPFAC in case user Adds
517           // reference annotations
518           String tftString = ArgParser
519                   .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
520                           Arg.TEMPFAC, Position.AFTER, av, subVals, null,
521                           null, null);
522           boolean notempfac = ArgParser.getFromSubValArgOrPref(avm,
523                   Arg.NOTEMPFAC, subVals, null, "ADD_TEMPFACT_ANN", false,
524                   true);
525           TFType tft = notempfac ? null : TFType.DEFAULT;
526           if (tftString != null && !notempfac)
527           {
528             // get kind of temperature factor annotation
529             try
530             {
531               tft = TFType.valueOf(tftString.toUpperCase(Locale.ROOT));
532               Console.debug("Obtained Temperature Factor type of '" + tft
533                       + "' for structure '" + structureFilepath + "'");
534             } catch (IllegalArgumentException e)
535             {
536               // Just an error message!
537               StringBuilder sb = new StringBuilder().append("Cannot set ")
538                       .append(Arg.TEMPFAC.argString()).append(" to '")
539                       .append(tft)
540                       .append("', ignoring.  Valid values are: ");
541               Iterator<TFType> it = Arrays.stream(TFType.values())
542                       .iterator();
543               while (it.hasNext())
544               {
545                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
546                 if (it.hasNext())
547                   sb.append(", ");
548               }
549               Console.warn(sb.toString());
550             }
551           }
552
553           String sViewer = ArgParser.getFromSubValArgOrPref(avm,
554                   Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, null,
555                   null, "jmol");
556           ViewerType viewerType = null;
557           if (!"none".equals(sViewer))
558           {
559             for (ViewerType v : EnumSet.allOf(ViewerType.class))
560             {
561               String name = v.name().toLowerCase(Locale.ROOT)
562                       .replaceAll(" ", "");
563               if (sViewer.equals(name))
564               {
565                 viewerType = v;
566                 break;
567               }
568             }
569           }
570
571           // TODO use ssFromStructure
572           StructureViewer sv = StructureChooser
573                   .openStructureFileForSequence(null, null, ap, seq, false,
574                           structureFilepath, tft, paeFilepath, false,
575                           ssFromStructure, false, viewerType);
576
577           if (sv == null)
578           {
579             Console.error("Failed to import and open structure view.");
580             continue;
581           }
582           try
583           {
584             long tries = 1000;
585             while (sv.isBusy() && tries > 0)
586             {
587               Thread.sleep(25);
588               if (sv.isBusy())
589               {
590                 tries--;
591                 Console.debug(
592                         "Waiting for viewer for " + structureFilepath);
593               }
594             }
595             if (tries == 0 && sv.isBusy())
596             {
597               Console.warn(
598                       "Gave up waiting for structure viewer to load. Something may have gone wrong.");
599             }
600           } catch (Exception x)
601           {
602             Console.warn("Exception whilst waiting for structure viewer "
603                     + structureFilepath, x);
604           }
605           Console.debug(
606                   "Successfully opened viewer for " + structureFilepath);
607           String structureImageFilename = ArgParser.getValueFromSubValOrArg(
608                   avm, av, Arg.STRUCTUREIMAGE, subVals);
609           if (sv != null && structureImageFilename != null)
610           {
611             ArgValue siAv = avm.getClosestNextArgValueOfArg(av,
612                     Arg.STRUCTUREIMAGE);
613             SubVals sisv = null;
614             if (structureImageFilename.equals(siAv.getValue()))
615             {
616               sisv = siAv.getSubVals();
617             }
618             File structureImageFile = new File(structureImageFilename);
619             String width = ArgParser.getValueFromSubValOrArg(avm, av,
620                     Arg.STRUCTUREIMAGEWIDTH, sisv);
621             String height = ArgParser.getValueFromSubValOrArg(avm, av,
622                     Arg.STRUCTUREIMAGEHEIGHT, sisv);
623             String scale = ArgParser.getValueFromSubValOrArg(avm, av,
624                     Arg.STRUCTUREIMAGESCALE, sisv);
625             String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
626                     Arg.STRUCTUREIMAGETEXTRENDERER, sisv);
627             String typeS = ArgParser.getValueFromSubValOrArg(avm, av,
628                     Arg.STRUCTUREIMAGETYPE, sisv);
629             if (typeS == null || typeS.length() == 0)
630             {
631               typeS = FileUtils.getExtension(structureImageFile);
632             }
633             TYPE imageType;
634             try
635             {
636               imageType = Enum.valueOf(TYPE.class,
637                       typeS.toUpperCase(Locale.ROOT));
638             } catch (IllegalArgumentException e)
639             {
640               Console.warn("Do not know image format '" + typeS
641                       + "', using PNG");
642               imageType = TYPE.PNG;
643             }
644             BitmapImageSizing userBis = ImageMaker
645                     .parseScaleWidthHeightStrings(scale, width, height);
646             // TODO MAKE THIS VIEWER INDEPENDENT!!
647             switch (StructureViewer.getViewerType())
648             {
649             case JMOL:
650               try
651               {
652                 Thread.sleep(1000); // WHY ???
653               } catch (InterruptedException e)
654               {
655                 // TODO Auto-generated catch block
656                 e.printStackTrace();
657               }
658               JalviewStructureDisplayI sview = sv
659                       .getJalviewStructureDisplay();
660               if (sview instanceof AppJmol)
661               {
662                 AppJmol jmol = (AppJmol) sview;
663                 try
664                 {
665                   Console.debug("Rendering image to " + structureImageFile);
666                   jmol.makePDBImage(structureImageFile, imageType, renderer,
667                           userBis);
668                   Console.debug("Finished Rendering image to "
669                           + structureImageFile);
670
671                 } catch (ImageOutputException ioexc)
672                 {
673                   Console.warn("Unexpected error whilst exporting image to "
674                           + structureImageFile, ioexc);
675                 }
676
677               }
678               break;
679             default:
680               Console.warn("Cannot export image for structure viewer "
681                       + sv.getViewerType() + " yet");
682               break;
683             }
684           }
685         }
686       }
687     }
688
689     if (wrap)
690     {
691       AlignFrame af = afMap.get(id);
692       if (af != null)
693       {
694         af.setWrapFormat(wrap);
695       }
696     }
697
698     /*
699     boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
700     if (doShading)
701     {
702       AlignFrame af = afMap.get(id);
703       for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
704               .findAnnotation(PDBChain.class.getName().toString()))
705       {
706         AnnotationColourGradient acg = new AnnotationColourGradient(aa,
707                 af.alignPanel.av.getGlobalColourScheme(), 0);
708         acg.setSeqAssociated(true);
709         af.changeColour(acg);
710         Console.info("Changed colour " + acg.toString());
711       }
712     }
713     */
714
715     return theseArgsWereParsed;
716   }
717
718   protected void processGroovyScript(String id)
719   {
720     ArgValuesMap avm = argParser.getLinkedArgs(id);
721     AlignFrame af = afMap.get(id);
722
723     if (af == null)
724     {
725       Console.warn("Did not have an alignment window for id=" + id);
726       return;
727     }
728
729     if (avm.containsArg(Arg.GROOVY))
730     {
731       String groovyscript = avm.getValue(Arg.GROOVY);
732       if (groovyscript != null)
733       {
734         // Execute the groovy script after we've done all the rendering stuff
735         // and before any images or figures are generated.
736         Console.info("Executing script " + groovyscript);
737         Jalview.getInstance().executeGroovyScript(groovyscript, af);
738       }
739     }
740   }
741
742   protected boolean processImages(String id)
743   {
744     ArgValuesMap avm = argParser.getLinkedArgs(id);
745     AlignFrame af = afMap.get(id);
746
747     if (af == null)
748     {
749       Console.warn("Did not have an alignment window for id=" + id);
750       return false;
751     }
752
753     if (avm.containsArg(Arg.IMAGE))
754     {
755       for (ArgValue av : avm.getArgValueList(Arg.IMAGE))
756       {
757         String val = av.getValue();
758         SubVals subVal = av.getSubVals();
759         String fileName = subVal.getContent();
760         File file = new File(fileName);
761         String name = af.getName();
762         String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
763                 Arg.TEXTRENDERER, subVal);
764         if (renderer == null)
765           renderer = "text";
766         String type = "png"; // default
767
768         String scale = ArgParser.getValueFromSubValOrArg(avm, av, Arg.SCALE,
769                 subVal);
770         String width = ArgParser.getValueFromSubValOrArg(avm, av, Arg.WIDTH,
771                 subVal);
772         String height = ArgParser.getValueFromSubValOrArg(avm, av,
773                 Arg.HEIGHT, subVal);
774         BitmapImageSizing userBis = ImageMaker
775                 .parseScaleWidthHeightStrings(scale, width, height);
776
777         type = ArgParser.getValueFromSubValOrArg(avm, av, Arg.TYPE, subVal);
778         if (type == null && fileName != null)
779         {
780           for (String ext : new String[] { "svg", "png", "html", "eps" })
781           {
782             if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
783             {
784               type = ext;
785             }
786           }
787         }
788         // for moment we disable JSON export
789         Cache.setPropsAreReadOnly(true);
790         Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
791
792         Console.info("Writing " + file);
793         try
794         {
795           switch (type)
796           {
797
798           case "svg":
799             Console.debug("Outputting type '" + type + "' to " + fileName);
800             af.createSVG(file, renderer);
801             break;
802
803           case "png":
804             Console.debug("Outputting type '" + type + "' to " + fileName);
805             af.createPNG(file, null, userBis);
806             break;
807
808           case "html":
809             Console.debug("Outputting type '" + type + "' to " + fileName);
810             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
811             htmlSVG.exportHTML(fileName, renderer);
812             break;
813
814           case "biojs":
815             Console.debug(
816                     "Creating BioJS MSA Viwer HTML file: " + fileName);
817             try
818             {
819               BioJsHTMLOutput.refreshVersionInfo(
820                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
821             } catch (URISyntaxException e)
822             {
823               e.printStackTrace();
824             }
825             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
826             bjs.exportHTML(fileName);
827             break;
828
829           case "eps":
830             Console.debug("Creating EPS file: " + fileName);
831             af.createEPS(file, name);
832             break;
833
834           case "imagemap":
835             Console.debug("Creating ImageMap file: " + fileName);
836             af.createImageMap(file, name);
837             break;
838
839           default:
840             Console.warn(Arg.IMAGE.argString() + " type '" + type
841                     + "' not known. Ignoring");
842             break;
843           }
844         } catch (Exception ioex)
845         {
846           Console.warn("Unexpected error during export", ioex);
847         }
848       }
849     }
850     return true;
851   }
852
853   protected boolean processOutput(String id)
854   {
855     ArgValuesMap avm = argParser.getLinkedArgs(id);
856     AlignFrame af = afMap.get(id);
857
858     if (af == null)
859     {
860       Console.warn("Did not have an alignment window for id=" + id);
861       return false;
862     }
863
864     if (avm.containsArg(Arg.OUTPUT))
865     {
866       for (ArgValue av : avm.getArgValueList(Arg.OUTPUT))
867       {
868         String val = av.getValue();
869         SubVals subVals = av.getSubVals();
870         String fileName = subVals.getContent();
871         File file = new File(fileName);
872         boolean overwrite = ArgParser.getFromSubValArgOrPref(avm,
873                 Arg.OVERWRITE, subVals, null, "OVERWRITE_OUTPUT", false);
874         // backups. Use the Arg.BACKUPS or subval "backups" setting first,
875         // otherwise if headless assume false, if not headless use the user
876         // preference with default true.
877         boolean backups = ArgParser.getFromSubValArgOrPref(avm, Arg.BACKUPS,
878                 subVals, null,
879                 Platform.isHeadless() ? null : BackupFiles.ENABLED,
880                 !Platform.isHeadless());
881
882         // if backups is not true then --overwrite must be specified
883         if (file.exists() && !(overwrite || backups))
884         {
885           Console.error("Won't overwrite file '" + fileName + "' without "
886                   + Arg.OVERWRITE.argString() + " or "
887                   + Arg.BACKUPS.argString() + " set");
888           return false;
889         }
890
891         String name = af.getName();
892         String format = ArgParser.getValueFromSubValOrArg(avm, av,
893                 Arg.FORMAT, subVals);
894         FileFormats ffs = FileFormats.getInstance();
895         List<String> validFormats = ffs.getWritableFormats(false);
896
897         FileFormatI ff = null;
898         if (format == null && fileName != null)
899         {
900           FORMAT: for (String fname : validFormats)
901           {
902             FileFormatI tff = ffs.forName(fname);
903             String[] extensions = tff.getExtensions().split(",");
904             for (String ext : extensions)
905             {
906               if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
907               {
908                 ff = tff;
909                 format = ff.getName();
910                 break FORMAT;
911               }
912             }
913           }
914         }
915         if (ff == null && format != null)
916         {
917           ff = ffs.forName(format);
918         }
919         if (ff == null)
920         {
921           StringBuilder validSB = new StringBuilder();
922           for (String f : validFormats)
923           {
924             if (validSB.length() > 0)
925               validSB.append(", ");
926             validSB.append(f);
927             FileFormatI tff = ffs.forName(f);
928             validSB.append(" (");
929             validSB.append(tff.getExtensions());
930             validSB.append(")");
931           }
932
933           Jalview.exit("No valid format specified for "
934                   + Arg.OUTPUT.argString() + ". Valid formats are "
935                   + validSB.toString() + ".", 1);
936           // this return really shouldn't happen
937           return false;
938         }
939
940         String savedBackupsPreference = Cache
941                 .getDefault(BackupFiles.ENABLED, null);
942         Console.debug("Setting backups to " + backups);
943         Cache.applicationProperties.put(BackupFiles.ENABLED,
944                 Boolean.toString(backups));
945
946         Console.info("Writing " + fileName);
947
948         af.saveAlignment(fileName, ff);
949         Console.debug("Returning backups to " + savedBackupsPreference);
950         if (savedBackupsPreference != null)
951           Cache.applicationProperties.put(BackupFiles.ENABLED,
952                   savedBackupsPreference);
953         if (af.isSaveAlignmentSuccessful())
954         {
955           Console.debug("Written alignment '" + name + "' in "
956                   + ff.getName() + " format to " + file);
957         }
958         else
959         {
960           Console.warn("Error writing file " + file + " in " + ff.getName()
961                   + " format!");
962         }
963
964       }
965     }
966     return true;
967   }
968
969   private SequenceI getSpecifiedSequence(AlignFrame af, ArgValuesMap avm,
970           ArgValue av)
971   {
972     SubVals subVals = av.getSubVals();
973     ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID);
974     SequenceI seq = null;
975     if (subVals == null && idAv == null)
976       return null;
977     if (af == null || af.getCurrentView() == null)
978     {
979       return null;
980     }
981     AlignmentI al = af.getCurrentView().getAlignment();
982     if (al == null)
983     {
984       return null;
985     }
986     if (subVals != null)
987     {
988       if (subVals.has(Arg.SEQID.getName()))
989       {
990         seq = al.findName(subVals.get(Arg.SEQID.getName()));
991       }
992       else if (-1 < subVals.getIndex()
993               && subVals.getIndex() < al.getSequences().size())
994       {
995         seq = al.getSequenceAt(subVals.getIndex());
996       }
997     }
998     else if (idAv != null)
999     {
1000       seq = al.findName(idAv.getValue());
1001     }
1002     return seq;
1003   }
1004 }