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