JAL-4160 --colour can now take user defined scheme names and colour scheme-spec defin...
[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.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 boolean commandArgsProvided = false;
70
71   private boolean argsWereParsed = false;
72
73   public Commands(ArgParser argparser, boolean headless)
74   {
75     this(Desktop.instance, argparser, headless);
76   }
77
78   public Commands(Desktop d, ArgParser argparser, boolean h)
79   {
80     argParser = argparser;
81     headless = h;
82     desktop = d;
83     afMap = new HashMap<>();
84     if (argparser != null)
85     {
86       processArgs(argparser, headless);
87     }
88   }
89
90   private boolean processArgs(ArgParser argparser, boolean h)
91   {
92     argParser = argparser;
93     headless = h;
94     boolean theseArgsWereParsed = false;
95
96     if (argParser != null && argParser.getLinkedIds() != null)
97     {
98       for (String id : argParser.getLinkedIds())
99       {
100         ArgValuesMap avm = argParser.getLinkedArgs(id);
101         theseArgsWereParsed = true;
102         theseArgsWereParsed &= processLinked(id);
103         processGroovyScript(id);
104         boolean processLinkedOkay = theseArgsWereParsed;
105         
106         // wait around until alignFrame isn't busy
107         AlignFrame af=afMap.get(id);
108         while (af!=null && af.getViewport().isCalcInProgress())
109         {
110           try {
111             Thread.sleep(25);
112           } catch (Exception q) {};
113         }
114         
115         theseArgsWereParsed &= processImages(id);
116         if (processLinkedOkay)
117           theseArgsWereParsed &= processOutput(id);
118
119         // close ap
120         if (avm.getBoolean(Arg.CLOSE))
121         {
122           af = afMap.get(id);
123           if (af != null)
124           {
125             af.closeMenuItem_actionPerformed(true);
126           }
127         }
128
129       }
130
131     }
132     if (argParser.getBoolean(Arg.QUIT))
133     {
134       Jalview.getInstance().quit();
135       return true;
136     }
137     // carry on with jalview.bin.Jalview
138     argsWereParsed = theseArgsWereParsed;
139     return argsWereParsed;
140   }
141
142   public boolean commandArgsProvided()
143   {
144     return commandArgsProvided;
145   }
146
147   public boolean argsWereParsed()
148   {
149     return argsWereParsed;
150   }
151
152   protected boolean processUnlinked(String id)
153   {
154     return processLinked(id);
155   }
156
157   protected boolean processLinked(String id)
158   {
159     boolean theseArgsWereParsed = false;
160     ArgValuesMap avm = argParser.getLinkedArgs(id);
161     if (avm == null)
162       return true;
163
164     /*
165      * // script to execute after all loading is completed one way or another String
166      * groovyscript = m.get(Arg.GROOVY) == null ? null :
167      * m.get(Arg.GROOVY).getValue(); String file = m.get(Arg.OPEN) == null ? null :
168      * m.get(Arg.OPEN).getValue(); String data = null; FileFormatI format = null;
169      * DataSourceType protocol = null;
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             }
221           }
222         }
223
224         DataSourceType protocol = AppletFormatAdapter
225                 .checkProtocol(openFile);
226
227         FileFormatI format = null;
228         try
229         {
230           format = new IdentifyFile().identify(openFile, protocol);
231         } catch (FileFormatException e1)
232         {
233           Console.error("Unknown file format for '" + openFile + "'");
234         }
235
236         af = afMap.get(id);
237         // When to open a new AlignFrame
238         if (af == null || "true".equals(av.getSubVal("new"))
239                 || a == Arg.OPEN || format == FileFormat.Jalview)
240         {
241           if (a == Arg.OPEN)
242           {
243             Jalview.testoutput(argParser, Arg.OPEN, "examples/uniref50.fa",
244                     openFile);
245           }
246
247           Console.debug(
248                   "Opening '" + openFile + "' in new alignment frame");
249           FileLoader fileLoader = new FileLoader(!headless);
250
251           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
252                   format);
253
254           // wrap alignment?
255           boolean wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv,
256                   null, "WRAP_ALIGNMENT", false);
257           af.getCurrentView().setWrapAlignment(wrap);
258
259           // colour alignment?
260           String colour = ArgParser.getFromSubValArgOrPref(avm, av,
261                   Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", "");
262           if ("" != colour)
263           {
264             ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
265                     af.getViewport(), af.getViewport().getAlignment(), colour);
266             
267             if (cs==null && !"None".equals(colour))
268             {
269               Console.warn("Couldn't parse '"+colour+"' as a colourscheme.");
270             } else {
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           // store the AlignFrame for this id
360           afMap.put(id, af);
361
362           // is it its own structure file?
363           if (format.isStructureFile())
364           {
365             StructureSelectionManager ssm = StructureSelectionManager
366                     .getStructureSelectionManager(Desktop.instance);
367             SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
368             ssm.computeMapping(false, new SequenceI[] { seq }, null,
369                     openFile, DataSourceType.FILE, null, null, null, false);
370           }
371         }
372         else
373         {
374           Console.debug(
375                   "Opening '" + openFile + "' in existing alignment frame");
376           DataSourceType dst = HttpUtils.startsWithHttpOrHttps(openFile)
377                   ? DataSourceType.URL
378                   : DataSourceType.FILE;
379           FileLoader fileLoader = new FileLoader(!headless);
380           fileLoader.LoadFile(af.getCurrentView(), openFile, dst, null,
381                   false);
382         }
383
384         Console.debug("Command " + Arg.APPEND + " executed successfully!");
385
386       }
387       if (first) // first=true means nothing opened
388       {
389         if (headless)
390         {
391           Jalview.exit("Could not open any files in headless mode", 1);
392         }
393         else
394         {
395           Console.warn("No more files to open");
396         }
397       }
398       if (progressBarSet && desktop != null)
399         desktop.setProgressBar(null, progress);
400
401     }
402
403     // open the structure (from same PDB file or given PDBfile)
404     if (!avm.getBoolean(Arg.NOSTRUCTURE))
405     {
406       AlignFrame af = afMap.get(id);
407       if (avm.containsArg(Arg.STRUCTURE))
408       {
409         commandArgsProvided = true;
410         for (ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
411         {
412           String val = av.getValue();
413           SubVals subVals = av.getSubVals();
414           SequenceI seq = getSpecifiedSequence(af, avm, av);
415           if (seq == null)
416           {
417             // Could not find sequence from subId, let's assume the first
418             // sequence in the alignframe
419             AlignmentI al = af.getCurrentView().getAlignment();
420             seq = al.getSequenceAt(0);
421           }
422
423           if (seq == null)
424           {
425             Console.warn("Could not find sequence for argument "
426                     + Arg.STRUCTURE.argString() + "=" + val);
427             // you probably want to continue here, not break
428             // break;
429             continue;
430           }
431           File structureFile = null;
432           if (subVals.getContent() != null
433                   && subVals.getContent().length() != 0)
434           {
435             structureFile = new File(subVals.getContent());
436             Console.debug("Using structure file (from argument) '"
437                     + structureFile.getAbsolutePath() + "'");
438           }
439           // TRY THIS
440           /*
441            * PDBEntry fileEntry = new AssociatePdbFileWithSeq()
442            * .associatePdbWithSeq(selectedPdbFileName, DataSourceType.FILE,
443            * selectedSequence, true, Desktop.instance);
444            * 
445            * sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap, new
446            * SequenceI[] { selectedSequence });
447            * 
448            */
449           /* THIS DOESN'T WORK */
450           else if (seq.getAllPDBEntries() != null
451                   && seq.getAllPDBEntries().size() > 0)
452           {
453             structureFile = new File(
454                     seq.getAllPDBEntries().elementAt(0).getFile());
455             Console.debug("Using structure file (from sequence) '"
456                     + structureFile.getAbsolutePath() + "'");
457           }
458
459           if (structureFile == null)
460           {
461             Console.warn("Not provided structure file with '" + val + "'");
462             continue;
463           }
464
465           if (!structureFile.exists())
466           {
467             Console.warn("Structure file '"
468                     + structureFile.getAbsoluteFile() + "' not found.");
469             continue;
470           }
471
472           Console.debug("Using structure file "
473                   + structureFile.getAbsolutePath());
474
475           // open structure view
476           AlignmentPanel ap = af.alignPanel;
477           if (headless)
478           {
479             Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
480                     StructureViewer.ViewerType.JMOL.toString());
481           }
482
483           String structureFilepath = structureFile.getAbsolutePath();
484
485           // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
486           String paeFilepath = ArgParser
487                   .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
488                           Arg.PAEMATRIX, Position.AFTER, av, subVals, null,
489                           null, null);
490           if (paeFilepath != null)
491           {
492             File paeFile = new File(paeFilepath);
493
494             try
495             {
496               paeFilepath = paeFile.getCanonicalPath();
497             } catch (IOException e)
498             {
499               paeFilepath = paeFile.getAbsolutePath();
500               Console.warn("Problem with the PAE file path: '"
501                       + paeFile.getPath() + "'");
502             }
503           }
504
505           // showing annotations from structure file or not
506           boolean ssFromStructure = ArgParser.getFromSubValArgOrPref(avm,
507                   Arg.SHOWSSANNOTATIONS, subVals, null, "STRUCT_FROM_PDB",
508                   true);
509
510           // get TEMPFAC type from subvals or Arg.TEMPFAC in case user Adds
511           // reference annotations
512           String tftString = ArgParser
513                   .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
514                           Arg.TEMPFAC, Position.AFTER, av, subVals, null,
515                           null, null);
516           boolean notempfac = ArgParser.getFromSubValArgOrPref(avm,
517                   Arg.NOTEMPFAC, subVals, null, "ADD_TEMPFACT_ANN", false,
518                   true);
519           TFType tft = notempfac ? null : TFType.DEFAULT;
520           if (tftString != null && !notempfac)
521           {
522             // get kind of temperature factor annotation
523             try
524             {
525               tft = TFType.valueOf(tftString.toUpperCase(Locale.ROOT));
526               Console.debug("Obtained Temperature Factor type of '" + tft
527                       + "' for structure '" + structureFilepath + "'");
528             } catch (IllegalArgumentException e)
529             {
530               // Just an error message!
531               StringBuilder sb = new StringBuilder().append("Cannot set ")
532                       .append(Arg.TEMPFAC.argString()).append(" to '")
533                       .append(tft)
534                       .append("', ignoring.  Valid values are: ");
535               Iterator<TFType> it = Arrays.stream(TFType.values())
536                       .iterator();
537               while (it.hasNext())
538               {
539                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
540                 if (it.hasNext())
541                   sb.append(", ");
542               }
543               Console.warn(sb.toString());
544             }
545           }
546
547           String sViewer = ArgParser.getFromSubValArgOrPref(avm,
548                   Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, null,
549                   null, "jmol");
550           ViewerType viewerType = null;
551           if (!"none".equals(sViewer))
552           {
553             for (ViewerType v : EnumSet.allOf(ViewerType.class))
554             {
555               String name = v.name().toLowerCase(Locale.ROOT)
556                       .replaceAll(" ", "");
557               if (sViewer.equals(name))
558               {
559                 viewerType = v;
560                 break;
561               }
562             }
563           }
564
565           // TODO use ssFromStructure
566           StructureViewer sv = StructureChooser
567                   .openStructureFileForSequence(null, null, ap, seq, false,
568                           structureFilepath, tft, paeFilepath, false,
569                           ssFromStructure, false, viewerType);
570
571           if (headless)
572           {
573             sv.setAsync(false);
574           }
575
576           String structureImageFilename = ArgParser.getValueFromSubValOrArg(
577                   avm, av, Arg.STRUCTUREIMAGE, subVals);
578           if (sv != null && structureImageFilename != null)
579           {
580             ArgValue siAv = avm.getClosestNextArgValueOfArg(av,
581                     Arg.STRUCTUREIMAGE);
582             SubVals sisv = null;
583             if (structureImageFilename.equals(siAv.getValue()))
584             {
585               sisv = siAv.getSubVals();
586             }
587             File structureImageFile = new File(structureImageFilename);
588             String width = ArgParser.getValueFromSubValOrArg(avm, av,
589                     Arg.STRUCTUREIMAGEWIDTH, sisv);
590             String height = ArgParser.getValueFromSubValOrArg(avm, av,
591                     Arg.STRUCTUREIMAGEHEIGHT, sisv);
592             String scale = ArgParser.getValueFromSubValOrArg(avm, av,
593                     Arg.STRUCTUREIMAGESCALE, sisv);
594             String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
595                     Arg.STRUCTUREIMAGETEXTRENDERER, sisv);
596             String typeS = ArgParser.getValueFromSubValOrArg(avm, av,
597                     Arg.STRUCTUREIMAGETYPE, sisv);
598             if (typeS == null || typeS.length() == 0)
599             {
600               typeS = FileUtils.getExtension(structureImageFile);
601             }
602             TYPE imageType;
603             try
604             {
605               imageType = Enum.valueOf(TYPE.class,
606                       typeS.toUpperCase(Locale.ROOT));
607             } catch (IllegalArgumentException e)
608             {
609               Console.warn("Do not know image format '" + typeS
610                       + "', using PNG");
611               imageType = TYPE.PNG;
612             }
613             BitmapImageSizing userBis = ImageMaker
614                     .parseScaleWidthHeightStrings(scale, width, height);
615             switch (StructureViewer.getViewerType())
616             {
617             case JMOL:
618               try
619               {
620                 Thread.sleep(1000);
621               } catch (InterruptedException e)
622               {
623                 // TODO Auto-generated catch block
624                 e.printStackTrace();
625               }
626               JalviewStructureDisplayI sview = sv
627                       .getJalviewStructureDisplay();
628               if (sview instanceof AppJmol)
629               {
630                 AppJmol jmol = (AppJmol) sview;
631                 jmol.makePDBImage(structureImageFile, imageType, renderer,
632                         userBis);
633               }
634               break;
635             default:
636               Console.warn("Cannot export image for structure viewer "
637                       + sv.getViewerType() + " yet");
638               break;
639             }
640           }
641         }
642       }
643     }
644
645     /*
646     boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
647     if (doShading)
648     {
649       AlignFrame af = afMap.get(id);
650       for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
651               .findAnnotation(PDBChain.class.getName().toString()))
652       {
653         AnnotationColourGradient acg = new AnnotationColourGradient(aa,
654                 af.alignPanel.av.getGlobalColourScheme(), 0);
655         acg.setSeqAssociated(true);
656         af.changeColour(acg);
657         Console.info("Changed colour " + acg.toString());
658       }
659     }
660     */
661
662     return theseArgsWereParsed;
663   }
664
665   protected void processGroovyScript(String id)
666   {
667     ArgValuesMap avm = argParser.getLinkedArgs(id);
668     AlignFrame af = afMap.get(id);
669
670     if (af == null)
671     {
672       Console.warn("Did not have an alignment window for id=" + id);
673       return;
674     }
675
676     if (avm.containsArg(Arg.GROOVY))
677     {
678       String groovyscript = avm.getValue(Arg.GROOVY);
679       if (groovyscript != null)
680       {
681         // Execute the groovy script after we've done all the rendering stuff
682         // and before any images or figures are generated.
683         Console.info("Executing script " + groovyscript);
684         Jalview.getInstance().executeGroovyScript(groovyscript, af);
685       }
686     }
687   }
688
689   protected boolean processImages(String id)
690   {
691     ArgValuesMap avm = argParser.getLinkedArgs(id);
692     AlignFrame af = afMap.get(id);
693
694     if (af == null)
695     {
696       Console.warn("Did not have an alignment window for id=" + id);
697       return false;
698     }
699
700     if (avm.containsArg(Arg.IMAGE))
701     {
702       for (ArgValue av : avm.getArgValueList(Arg.IMAGE))
703       {
704         String val = av.getValue();
705         SubVals subVal = av.getSubVals();
706         String fileName = subVal.getContent();
707         File file = new File(fileName);
708         String name = af.getName();
709         String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
710                 Arg.TEXTRENDERER, subVal);
711         if (renderer == null)
712           renderer = "text";
713         String type = "png"; // default
714
715         String scale = ArgParser.getValueFromSubValOrArg(avm, av, Arg.SCALE,
716                 subVal);
717         String width = ArgParser.getValueFromSubValOrArg(avm, av, Arg.WIDTH,
718                 subVal);
719         String height = ArgParser.getValueFromSubValOrArg(avm, av,
720                 Arg.HEIGHT, subVal);
721         BitmapImageSizing userBis = ImageMaker
722                 .parseScaleWidthHeightStrings(scale, width, height);
723
724         type = ArgParser.getValueFromSubValOrArg(avm, av, Arg.TYPE, subVal);
725         if (type == null && fileName != null)
726         {
727           for (String ext : new String[] { "svg", "png", "html", "eps" })
728           {
729             if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
730             {
731               type = ext;
732             }
733           }
734         }
735         // for moment we disable JSON export
736         Cache.setPropsAreReadOnly(true);
737         Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
738
739         Console.info("Writing " + file);
740
741         switch (type)
742         {
743
744         case "svg":
745           Console.debug("Outputting type '" + type + "' to " + fileName);
746           af.createSVG(file, renderer);
747           break;
748
749         case "png":
750           Console.debug("Outputting type '" + type + "' to " + fileName);
751           af.createPNG(file, null, userBis);
752           break;
753
754         case "html":
755           Console.debug("Outputting type '" + type + "' to " + fileName);
756           HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
757           htmlSVG.exportHTML(fileName, renderer);
758           break;
759
760         case "biojs":
761           Console.debug("Creating BioJS MSA Viwer HTML file: " + fileName);
762           try
763           {
764             BioJsHTMLOutput.refreshVersionInfo(
765                     BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
766           } catch (URISyntaxException e)
767           {
768             e.printStackTrace();
769           }
770           BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
771           bjs.exportHTML(fileName);
772           break;
773
774         case "eps":
775           Console.debug("Creating EPS file: " + fileName);
776           af.createEPS(file, name);
777           break;
778
779         case "imagemap":
780           Console.debug("Creating ImageMap file: " + fileName);
781           af.createImageMap(file, name);
782           break;
783
784         default:
785           Console.warn(Arg.IMAGE.argString() + " type '" + type
786                   + "' not known. Ignoring");
787           break;
788         }
789       }
790     }
791     return true;
792   }
793
794   protected boolean processOutput(String id)
795   {
796     ArgValuesMap avm = argParser.getLinkedArgs(id);
797     AlignFrame af = afMap.get(id);
798
799     if (af == null)
800     {
801       Console.warn("Did not have an alignment window for id=" + id);
802       return false;
803     }
804
805     if (avm.containsArg(Arg.OUTPUT))
806     {
807       for (ArgValue av : avm.getArgValueList(Arg.OUTPUT))
808       {
809         String val = av.getValue();
810         SubVals subVals = av.getSubVals();
811         String fileName = subVals.getContent();
812         File file = new File(fileName);
813         boolean overwrite = ArgParser.getFromSubValArgOrPref(avm,
814                 Arg.OVERWRITE, subVals, null, "OVERWRITE_OUTPUT", false);
815         // backups. Use the Arg.BACKUPS or subval "backups" setting first,
816         // otherwise if headless assume false, if not headless use the user
817         // preference with default true.
818         boolean backups = ArgParser.getFromSubValArgOrPref(avm, Arg.BACKUPS,
819                 subVals, null,
820                 Platform.isHeadless() ? null : BackupFiles.ENABLED,
821                 !Platform.isHeadless());
822
823         // if backups is not true then --overwrite must be specified
824         if (file.exists() && !(overwrite || backups))
825         {
826           Console.error("Won't overwrite file '" + fileName + "' without "
827                   + Arg.OVERWRITE.argString() + " or "
828                   + Arg.BACKUPS.argString() + " set");
829           return false;
830         }
831
832         String name = af.getName();
833         String format = ArgParser.getValueFromSubValOrArg(avm, av,
834                 Arg.FORMAT, subVals);
835         FileFormats ffs = FileFormats.getInstance();
836         List<String> validFormats = ffs.getWritableFormats(false);
837
838         FileFormatI ff = null;
839         if (format == null && fileName != null)
840         {
841           FORMAT: for (String fname : validFormats)
842           {
843             FileFormatI tff = ffs.forName(fname);
844             String[] extensions = tff.getExtensions().split(",");
845             for (String ext : extensions)
846             {
847               if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
848               {
849                 ff = tff;
850                 format = ff.getName();
851                 break FORMAT;
852               }
853             }
854           }
855         }
856         if (ff == null && format != null)
857         {
858           ff = ffs.forName(format);
859         }
860         if (ff == null)
861         {
862           StringBuilder validSB = new StringBuilder();
863           for (String f : validFormats)
864           {
865             if (validSB.length() > 0)
866               validSB.append(", ");
867             validSB.append(f);
868             FileFormatI tff = ffs.forName(f);
869             validSB.append(" (");
870             validSB.append(tff.getExtensions());
871             validSB.append(")");
872           }
873
874           Jalview.exit("No valid format specified for "
875                   + Arg.OUTPUT.argString() + ". Valid formats are "
876                   + validSB.toString() + ".", 1);
877           // this return really shouldn't happen
878           return false;
879         }
880
881         String savedBackupsPreference = Cache
882                 .getDefault(BackupFiles.ENABLED, null);
883         Console.debug("Setting backups to " + backups);
884         Cache.applicationProperties.put(BackupFiles.ENABLED,
885                 Boolean.toString(backups));
886
887         Console.info("Writing " + fileName);
888
889         af.saveAlignment(fileName, ff);
890         Console.debug("Returning backups to " + savedBackupsPreference);
891         if (savedBackupsPreference != null)
892           Cache.applicationProperties.put(BackupFiles.ENABLED,
893                   savedBackupsPreference);
894         if (af.isSaveAlignmentSuccessful())
895         {
896           Console.debug("Written alignment '" + name + "' in "
897                   + ff.getName() + " format to " + file);
898         }
899         else
900         {
901           Console.warn("Error writing file " + file + " in " + ff.getName()
902                   + " format!");
903         }
904
905       }
906     }
907     return true;
908   }
909
910   private SequenceI getSpecifiedSequence(AlignFrame af, ArgValuesMap avm,
911           ArgValue av)
912   {
913     SubVals subVals = av.getSubVals();
914     ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID);
915     SequenceI seq = null;
916     if (subVals == null && idAv == null)
917       return null;
918     if (af == null || af.getCurrentView() == null)
919     {
920       return null;
921     }
922     AlignmentI al = af.getCurrentView().getAlignment();
923     if (al == null)
924     {
925       return null;
926     }
927     if (subVals != null)
928     {
929       if (subVals.has(Arg.SEQID.getName()))
930       {
931         seq = al.findName(subVals.get(Arg.SEQID.getName()));
932       }
933       else if (-1 < subVals.getIndex()
934               && subVals.getIndex() < al.getSequences().size())
935       {
936         seq = al.getSequenceAt(subVals.getIndex());
937       }
938     }
939     else if (idAv != null)
940     {
941       seq = al.findName(idAv.getValue());
942     }
943     return seq;
944   }
945 }