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