Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:38:20 GMT
  2. Package jalview.bin

File Commands.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
40% of files have more coverage

Code metrics

230
541
25
1
1,431
1,164
197
0.36
21.64
25
7.88

Classes

Class Line # Actions
Commands 86 541 197
0.4007537740.1%
 

Contributing tests

This file is covered by 23 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.bin;
22   
23    import java.awt.Color;
24    import java.io.File;
25    import java.io.FileNotFoundException;
26    import java.io.IOException;
27    import java.net.URISyntaxException;
28    import java.util.ArrayList;
29    import java.util.Arrays;
30    import java.util.Collections;
31    import java.util.HashMap;
32    import java.util.Iterator;
33    import java.util.List;
34    import java.util.Locale;
35    import java.util.Map;
36   
37    import javax.swing.SwingUtilities;
38   
39    import jalview.analysis.AlignmentUtils;
40    import jalview.api.structures.JalviewStructureDisplayI;
41    import jalview.bin.Jalview.ExitCode;
42    import jalview.bin.argparser.Arg;
43    import jalview.bin.argparser.ArgParser;
44    import jalview.bin.argparser.ArgValue;
45    import jalview.bin.argparser.ArgValuesMap;
46    import jalview.bin.argparser.SubVals;
47    import jalview.datamodel.AlignmentI;
48    import jalview.datamodel.SequenceI;
49    import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
50    import jalview.gui.AlignFrame;
51    import jalview.gui.AlignmentPanel;
52    import jalview.gui.AppJmol;
53    import jalview.gui.Desktop;
54    import jalview.gui.Preferences;
55    import jalview.gui.StructureChooser;
56    import jalview.gui.StructureViewer;
57    import jalview.gui.StructureViewer.ViewerType;
58    import jalview.io.AppletFormatAdapter;
59    import jalview.io.BackupFiles;
60    import jalview.io.BioJsHTMLOutput;
61    import jalview.io.DataSourceType;
62    import jalview.io.FileFormat;
63    import jalview.io.FileFormatException;
64    import jalview.io.FileFormatI;
65    import jalview.io.FileFormats;
66    import jalview.io.FileLoader;
67    import jalview.io.HtmlSvgOutput;
68    import jalview.io.IdentifyFile;
69    import jalview.io.NewickFile;
70    import jalview.io.exceptions.ImageOutputException;
71    import jalview.schemes.ColourSchemeI;
72    import jalview.schemes.ColourSchemeProperty;
73    import jalview.structure.StructureCommandI;
74    import jalview.structure.StructureImportSettings.TFType;
75    import jalview.structure.StructureSelectionManager;
76    import jalview.util.ColorUtils;
77    import jalview.util.FileUtils;
78    import jalview.util.HttpUtils;
79    import jalview.util.ImageMaker;
80    import jalview.util.ImageMaker.TYPE;
81    import jalview.util.MessageManager;
82    import jalview.util.Platform;
83    import jalview.util.StringUtils;
84    import jalview.util.imagemaker.BitmapImageSizing;
85   
 
86    public class Commands
87    {
88    Desktop desktop;
89   
90    private boolean headless;
91   
92    private ArgParser argParser;
93   
94    private Map<String, AlignFrame> afMap;
95   
96    private Map<String, List<StructureViewer>> svMap;
97   
98    private boolean commandArgsProvided = false;
99   
100    private boolean argsWereParsed = false;
101   
102    private List<String> errors = new ArrayList<>();
103   
 
104  47 toggle public Commands(ArgParser argparser, boolean headless)
105    {
106  47 this(Desktop.instance, argparser, headless);
107    }
108   
 
109  47 toggle public Commands(Desktop d, ArgParser argparser, boolean h)
110    {
111  47 argParser = argparser;
112  47 headless = h;
113  47 desktop = d;
114  47 afMap = new HashMap<>();
115    }
116   
 
117  47 toggle protected boolean processArgs()
118    {
119  47 if (argParser == null)
120    {
121  0 return true;
122    }
123   
124  47 boolean theseArgsWereParsed = false;
125   
126  47 if (argParser != null && argParser.getLinkedIds() != null)
127    {
128  47 for (String id : argParser.getLinkedIds())
129    {
130  76 ArgValuesMap avm = argParser.getLinkedArgs(id);
131  76 theseArgsWereParsed = true;
132  76 boolean processLinkedOkay = processLinked(id);
133  72 theseArgsWereParsed &= processLinkedOkay;
134   
135  72 processGroovyScript(id);
136   
137    // wait around until alignFrame isn't busy
138  72 AlignFrame af = afMap.get(id);
139  96 while (af != null && af.getViewport().isCalcInProgress())
140    {
141  24 try
142    {
143  24 Thread.sleep(25);
144    } catch (Exception q)
145    {
146    }
147  24 ;
148    }
149   
150  72 theseArgsWereParsed &= processImages(id);
151   
152  72 if (processLinkedOkay)
153    {
154  72 theseArgsWereParsed &= processOutput(id);
155    }
156   
157    // close ap
158  72 if (avm.getBoolean(Arg.CLOSE))
159    {
160  60 af = afMap.get(id);
161  60 if (af != null)
162    {
163  60 af.closeMenuItem_actionPerformed(true);
164    }
165    }
166   
167    }
168   
169    }
170   
171    // report errors - if any
172  43 String errorsRaised = errorsToString();
173  43 if (errorsRaised.trim().length() > 0)
174    {
175  0 Console.warn(
176    "The following errors and warnings occurred whilst processing files:\n"
177    + errorsRaised);
178    }
179    // gui errors reported in Jalview
180   
181  43 if (argParser.getBoolean(Arg.QUIT))
182    {
183  0 Jalview.exit("Exiting due to " + Arg.QUIT.argString() + " argument.",
184    ExitCode.OK);
185  0 return true;
186    }
187    // carry on with jalview.bin.Jalview
188  43 argsWereParsed = theseArgsWereParsed;
189  43 return argsWereParsed;
190    }
191   
 
192  0 toggle public boolean commandArgsProvided()
193    {
194  0 return commandArgsProvided;
195    }
196   
 
197  43 toggle public boolean argsWereParsed()
198    {
199  43 return argsWereParsed;
200    }
201   
 
202  76 toggle protected boolean processLinked(String id)
203    {
204  76 boolean theseArgsWereParsed = false;
205  76 ArgValuesMap avm = argParser.getLinkedArgs(id);
206  76 if (avm == null)
207    {
208  0 return true;
209    }
210   
211  76 Boolean isError = Boolean.valueOf(false);
212   
213    // set wrap, showSSAnnotations, showAnnotations and hideTFrows scope here so
214    // it can be applied after structures are opened
215  76 boolean wrap = false;
216  76 boolean showSSAnnotations = false;
217  76 boolean showAnnotations = false;
218  76 boolean hideTFrows = false;
219  76 AlignFrame af = null;
220   
221  76 if (avm.containsArg(Arg.APPEND) || avm.containsArg(Arg.OPEN))
222    {
223  76 commandArgsProvided = true;
224  76 final long progress = System.currentTimeMillis();
225   
226  76 boolean first = true;
227  76 boolean progressBarSet = false;
228    // Combine the APPEND and OPEN files into one list, along with whether it
229    // was APPEND or OPEN
230  76 List<ArgValue> openAvList = new ArrayList<>();
231  76 openAvList.addAll(avm.getArgValueList(Arg.OPEN));
232  76 openAvList.addAll(avm.getArgValueList(Arg.APPEND));
233    // sort avlist based on av.getArgIndex()
234  76 Collections.sort(openAvList);
235  76 for (ArgValue av : openAvList)
236    {
237  88 Arg a = av.getArg();
238  88 SubVals sv = av.getSubVals();
239  88 String openFile0 = av.getValue();
240  88 String openFile = HttpUtils.equivalentJalviewUrl(openFile0);
241  88 if (openFile == null)
242  0 continue;
243   
244  88 theseArgsWereParsed = true;
245  88 if (first)
246    {
247  76 first = false;
248  76 if (!headless && desktop != null)
249    {
250  76 SwingUtilities.invokeLater(new Runnable()
251    {
 
252  76 toggle @Override
253    public void run()
254    {
255  76 desktop.setProgressBar(
256    MessageManager.getString(
257    "status.processing_commandline_args"),
258    progress);
259   
260    }
261    });
262  76 progressBarSet = true;
263    }
264    }
265   
266  88 if (!Platform.isJS())
267    /**
268    * ignore in JavaScript -- can't just file existence - could load it?
269    *
270    * @j2sIgnore
271    */
272    {
273  88 if (!HttpUtils.isPlausibleUri(openFile))
274    {
275  88 if (!(new File(openFile)).exists())
276    {
277  0 addError("Can't find file '" + openFile + "'");
278  0 isError = true;
279  0 continue;
280    }
281    }
282    }
283   
284  88 DataSourceType protocol = AppletFormatAdapter
285    .checkProtocol(openFile);
286   
287  88 FileFormatI format = null;
288  88 try
289    {
290  88 format = new IdentifyFile().identify(openFile, protocol);
291    } catch (FileNotFoundException e0)
292    {
293  0 addError((protocol == DataSourceType.URL ? "File at URL" : "File")
294    + " '" + openFile + "' not found");
295  0 isError = true;
296  0 continue;
297    } catch (FileFormatException e1)
298    {
299  0 addError("Unknown file format for '" + openFile + "'");
300  0 isError = true;
301  0 continue;
302    }
303   
304  88 af = afMap.get(id);
305    // When to open a new AlignFrame
306  88 if (af == null || "true".equals(av.getSubVal("new"))
307    || a == Arg.OPEN || format == FileFormat.Jalview)
308    {
309  76 if (a == Arg.OPEN)
310    {
311  67 Jalview.testoutput(argParser, Arg.OPEN, "examples/uniref50.fa",
312    openFile);
313    }
314   
315  76 Console.debug(
316    "Opening '" + openFile + "' in new alignment frame");
317  76 FileLoader fileLoader = new FileLoader(!headless);
318  76 boolean xception = false;
319  76 try
320    {
321  76 af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
322    format);
323  72 if (!openFile.equals(openFile0))
324    {
325  0 af.setTitle(openFile0);
326    }
327    } catch (Throwable thr)
328    {
329  0 xception = true;
330  0 addError("Couldn't open '" + openFile + "' as " + format + " "
331    + thr.getLocalizedMessage()
332    + " (Enable debug for full stack trace)");
333  0 isError = true;
334  0 Console.debug("Exception when opening '" + openFile + "'", thr);
335    } finally
336    {
337  72 if (af == null && !xception)
338    {
339  0 addInfo("Ignoring '" + openFile
340    + "' - no alignment data found.");
341  0 continue;
342    }
343    }
344   
345    // colour alignment
346  72 String colour = null;
347  72 if (avm.containsArg(Arg.COLOUR)
348    || !(format == FileFormat.Jalview))
349    {
350  72 colour = avm.getFromSubValArgOrPref(av, Arg.COLOUR, sv, null,
351    "DEFAULT_COLOUR_PROT", null);
352    }
353  72 if (colour != null)
354    {
355  0 this.colourAlignFrame(af, colour);
356    }
357   
358    // Change alignment frame title
359  72 String title = avm.getFromSubValArgOrPref(av, Arg.TITLE, sv, null,
360    null, null);
361  72 if (title != null)
362    {
363  0 af.setTitle(title);
364  0 Jalview.testoutput(argParser, Arg.TITLE, "test title", title);
365    }
366   
367    // Add features
368  72 String featuresfile = avm.getValueFromSubValOrArg(av,
369    Arg.FEATURES, sv);
370  72 if (featuresfile != null)
371    {
372  0 af.parseFeaturesFile(featuresfile,
373    AppletFormatAdapter.checkProtocol(featuresfile));
374  0 Jalview.testoutput(argParser, Arg.FEATURES,
375    "examples/testdata/plantfdx.features", featuresfile);
376    }
377   
378    // Add annotations from file
379  72 String annotationsfile = avm.getValueFromSubValOrArg(av,
380    Arg.ANNOTATIONS, sv);
381  72 if (annotationsfile != null)
382    {
383  0 af.loadJalviewDataFile(annotationsfile, null, null, null);
384  0 Jalview.testoutput(argParser, Arg.ANNOTATIONS,
385    "examples/testdata/plantfdx.annotations",
386    annotationsfile);
387    }
388   
389    // Set or clear the sortbytree flag
390  72 boolean sortbytree = avm.getBoolFromSubValOrArg(Arg.SORTBYTREE,
391    sv);
392  72 if (sortbytree)
393    {
394  0 af.getViewport().setSortByTree(true);
395  0 Jalview.testoutput(argParser, Arg.SORTBYTREE);
396    }
397   
398    // Load tree from file
399  72 String treefile = avm.getValueFromSubValOrArg(av, Arg.TREE, sv);
400  72 if (treefile != null)
401    {
402  0 try
403    {
404  0 NewickFile nf = new NewickFile(treefile,
405    AppletFormatAdapter.checkProtocol(treefile));
406  0 af.getViewport().setCurrentTree(
407    af.showNewickTree(nf, treefile).getTree());
408  0 Jalview.testoutput(argParser, Arg.TREE,
409    "examples/testdata/uniref50_test_tree", treefile);
410    } catch (IOException e)
411    {
412  0 addError("Couldn't add tree " + treefile, e);
413  0 isError = true;
414    }
415    }
416   
417    // Show secondary structure annotations?
418  72 showSSAnnotations = avm.getFromSubValArgOrPref(
419    Arg.SHOWSSANNOTATIONS, av.getSubVals(), null,
420    "STRUCT_FROM_PDB", true);
421    // Show sequence annotations?
422  72 showAnnotations = avm.getFromSubValArgOrPref(Arg.SHOWANNOTATIONS,
423    av.getSubVals(), null, "SHOW_ANNOTATIONS", true);
424    // hide the Temperature Factor row?
425  72 hideTFrows = (avm.getBoolean(Arg.NOTEMPFAC));
426   
427    // showSSAnnotations, showAnnotations, hideTFrows used after opening
428    // structure
429   
430    // wrap alignment? do this last for formatting reasons
431  72 wrap = avm.getFromSubValArgOrPref(Arg.WRAP, sv, null,
432    "WRAP_ALIGNMENT", false);
433    // af.setWrapFormat(wrap) is applied after structures are opened for
434    // annotation reasons
435   
436    // store the AlignFrame for this id
437  72 afMap.put(id, af);
438   
439    // is it its own structure file?
440  72 if (format.isStructureFile())
441    {
442  0 StructureSelectionManager ssm = StructureSelectionManager
443    .getStructureSelectionManager(Desktop.instance);
444  0 SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
445  0 ssm.computeMapping(false, new SequenceI[] { seq }, null,
446    openFile, DataSourceType.FILE, null, null, null, false);
447    }
448    }
449    else
450    {
451  12 Console.debug(
452    "Opening '" + openFile + "' in existing alignment frame");
453   
454  12 DataSourceType dst = HttpUtils.startsWithHttpOrHttps(openFile)
455    ? DataSourceType.URL
456    : DataSourceType.FILE;
457   
458  12 FileLoader fileLoader = new FileLoader(!headless);
459  12 fileLoader.LoadFile(af.getCurrentView(), openFile, dst, null,
460    false);
461    }
462   
463  84 Console.debug("Command " + Arg.APPEND + " executed successfully!");
464   
465    }
466  72 if (first) // first=true means nothing opened
467    {
468  0 if (headless)
469    {
470  0 Jalview.exit("Could not open any files in headless mode",
471    ExitCode.NO_FILES);
472    }
473    else
474    {
475  0 Console.info("No more files to open");
476    }
477    }
478  72 if (progressBarSet && desktop != null)
479  72 desktop.setProgressBar(null, progress);
480   
481    }
482   
483    // open the structure (from same PDB file or given PDBfile)
484  72 if (!avm.getBoolean(Arg.NOSTRUCTURE))
485    {
486  72 if (af == null)
487    {
488  0 af = afMap.get(id);
489    }
490  72 if (avm.containsArg(Arg.STRUCTURE))
491    {
492  0 commandArgsProvided = true;
493  0 for (ArgValue structureAv : avm.getArgValueList(Arg.STRUCTURE))
494    {
495  0 argParser.setStructureFilename(null);
496  0 String val = structureAv.getValue();
497  0 SubVals subVals = structureAv.getSubVals();
498  0 int argIndex = structureAv.getArgIndex();
499  0 SequenceI seq = getSpecifiedSequence(af, avm, structureAv);
500  0 if (seq == null)
501    {
502    // Could not find sequence from subId, let's assume the first
503    // sequence in the alignframe
504  0 AlignmentI al = af.getCurrentView().getAlignment();
505  0 seq = al.getSequenceAt(0);
506    }
507   
508  0 if (seq == null)
509    {
510  0 addWarn("Could not find sequence for argument "
511    + Arg.STRUCTURE.argString() + "=" + val);
512  0 continue;
513    }
514  0 String structureFilename = null;
515  0 File structureFile = null;
516  0 if (subVals.getContent() != null
517    && subVals.getContent().length() != 0)
518    {
519  0 structureFilename = subVals.getContent();
520  0 Console.debug("Using structure file (from argument) '"
521    + structureFilename + "'");
522  0 structureFile = new File(structureFilename);
523    }
524    /* THIS DOESN'T WORK */
525  0 else if (seq.getAllPDBEntries() != null
526    && seq.getAllPDBEntries().size() > 0)
527    {
528  0 structureFile = new File(
529    seq.getAllPDBEntries().elementAt(0).getFile());
530  0 if (structureFile != null)
531    {
532  0 Console.debug("Using structure file (from sequence) '"
533    + structureFile.getAbsolutePath() + "'");
534    }
535  0 structureFilename = structureFile.getAbsolutePath();
536    }
537   
538  0 if (structureFilename == null || structureFile == null)
539    {
540  0 addWarn("Not provided structure file with '" + val + "'");
541  0 continue;
542    }
543   
544  0 if (!structureFile.exists())
545    {
546  0 addWarn("Structure file '" + structureFile.getAbsoluteFile()
547    + "' not found.");
548  0 continue;
549    }
550   
551  0 Console.debug("Using structure file "
552    + structureFile.getAbsolutePath());
553   
554  0 argParser.setStructureFilename(structureFilename);
555   
556    // open structure view
557  0 AlignmentPanel ap = af.alignPanel;
558  0 if (headless)
559    {
560  0 Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
561    StructureViewer.ViewerType.JMOL.toString());
562    }
563   
564  0 String structureFilepath = structureFile.getAbsolutePath();
565   
566    // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
567  0 String paeFilepath = avm.getFromSubValArgOrPrefWithSubstitutions(
568    argParser, Arg.PAEMATRIX, ArgValuesMap.Position.AFTER,
569    structureAv, subVals, null, null, null);
570  0 if (paeFilepath != null)
571    {
572  0 File paeFile = new File(paeFilepath);
573   
574  0 try
575    {
576  0 paeFilepath = paeFile.getCanonicalPath();
577    } catch (IOException e)
578    {
579  0 paeFilepath = paeFile.getAbsolutePath();
580  0 addWarn("Problem with the PAE file path: '"
581    + paeFile.getPath() + "'");
582    }
583    }
584   
585    // showing annotations from structure file or not
586  0 boolean ssFromStructure = avm.getFromSubValArgOrPref(
587    Arg.SHOWSSANNOTATIONS, subVals, null, "STRUCT_FROM_PDB",
588    true);
589   
590    // get TEMPFAC type from subvals or Arg.TEMPFAC in case user Adds
591    // reference annotations
592  0 String tftString = avm.getFromSubValArgOrPrefWithSubstitutions(
593    argParser, Arg.TEMPFAC, ArgValuesMap.Position.AFTER,
594    structureAv, subVals, null, null, null);
595  0 boolean notempfac = avm.getFromSubValArgOrPref(Arg.NOTEMPFAC,
596    subVals, null, "ADD_TEMPFACT_ANN", false, true);
597  0 TFType tft = notempfac ? null : TFType.DEFAULT;
598  0 if (tftString != null && !notempfac)
599    {
600    // get kind of temperature factor annotation
601  0 try
602    {
603  0 tft = TFType.valueOf(tftString.toUpperCase(Locale.ROOT));
604  0 Console.debug("Obtained Temperature Factor type of '" + tft
605    + "' for structure '" + structureFilepath + "'");
606    } catch (IllegalArgumentException e)
607    {
608    // Just an error message!
609  0 StringBuilder sb = new StringBuilder().append("Cannot set ")
610    .append(Arg.TEMPFAC.argString()).append(" to '")
611    .append(tft)
612    .append("', ignoring. Valid values are: ");
613  0 Iterator<TFType> it = Arrays.stream(TFType.values())
614    .iterator();
615  0 while (it.hasNext())
616    {
617  0 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
618  0 if (it.hasNext())
619  0 sb.append(", ");
620    }
621  0 addWarn(sb.toString());
622    }
623    }
624   
625  0 String sViewerName = avm.getFromSubValArgOrPref(
626    Arg.STRUCTUREVIEWER, ArgValuesMap.Position.AFTER,
627    structureAv, subVals, null, null, "jmol");
628  0 ViewerType viewerType = ViewerType.getFromString(sViewerName);
629   
630    // TODO use ssFromStructure
631  0 StructureViewer structureViewer = StructureChooser
632    .openStructureFileForSequence(null, null, ap, seq, false,
633    structureFilepath, tft, paeFilepath, false,
634    ssFromStructure, false, viewerType);
635   
636  0 if (structureViewer == null)
637    {
638  0 if (!StringUtils.equalsIgnoreCase(sViewerName, "none"))
639    {
640  0 addError("Failed to import and open structure view for file '"
641    + structureFile + "'.");
642    }
643  0 continue;
644    }
645  0 try
646    {
647  0 long tries = 1000;
648  0 while (structureViewer.isBusy() && tries > 0)
649    {
650  0 Thread.sleep(25);
651  0 if (structureViewer.isBusy())
652    {
653  0 tries--;
654  0 Console.debug(
655    "Waiting for viewer for " + structureFilepath);
656    }
657    }
658  0 if (tries == 0 && structureViewer.isBusy())
659    {
660  0 addWarn("Gave up waiting for structure viewer to load file '"
661    + structureFile
662    + "'. Something may have gone wrong.");
663    }
664    } catch (Exception x)
665    {
666  0 addError("Exception whilst waiting for structure viewer "
667    + structureFilepath, x);
668  0 isError = true;
669    }
670   
671    // add StructureViewer to svMap list
672  0 if (svMap == null)
673    {
674  0 svMap = new HashMap<>();
675    }
676  0 if (svMap.get(id) == null)
677    {
678  0 svMap.put(id, new ArrayList<>());
679    }
680  0 svMap.get(id).add(structureViewer);
681   
682  0 Console.debug(
683    "Successfully opened viewer for " + structureFilepath);
684   
685  0 if (avm.containsArg(Arg.STRUCTUREIMAGE))
686    {
687  0 for (ArgValue structureImageArgValue : avm
688    .getArgValueListFromSubValOrArg(structureAv,
689    Arg.STRUCTUREIMAGE, subVals))
690    {
691  0 String structureImageFilename = argParser.makeSubstitutions(
692    structureImageArgValue.getValue(), id, true);
693  0 if (structureViewer != null && structureImageFilename != null)
694    {
695  0 SubVals structureImageSubVals = null;
696  0 structureImageSubVals = structureImageArgValue.getSubVals();
697  0 File structureImageFile = new File(structureImageFilename);
698  0 String width = avm.getValueFromSubValOrArg(
699    structureImageArgValue, Arg.WIDTH,
700    structureImageSubVals);
701  0 String height = avm.getValueFromSubValOrArg(
702    structureImageArgValue, Arg.HEIGHT,
703    structureImageSubVals);
704  0 String scale = avm.getValueFromSubValOrArg(
705    structureImageArgValue, Arg.SCALE,
706    structureImageSubVals);
707  0 String renderer = avm.getValueFromSubValOrArg(
708    structureImageArgValue, Arg.TEXTRENDERER,
709    structureImageSubVals);
710  0 String typeS = avm.getValueFromSubValOrArg(
711    structureImageArgValue, Arg.TYPE,
712    structureImageSubVals);
713  0 if (typeS == null || typeS.length() == 0)
714    {
715  0 typeS = FileUtils.getExtension(structureImageFile);
716    }
717  0 TYPE imageType;
718  0 try
719    {
720  0 imageType = Enum.valueOf(TYPE.class,
721    typeS.toUpperCase(Locale.ROOT));
722    } catch (IllegalArgumentException e)
723    {
724  0 addWarn("Do not know image format '" + typeS
725    + "', using PNG");
726  0 imageType = TYPE.PNG;
727    }
728  0 BitmapImageSizing userBis = ImageMaker
729    .parseScaleWidthHeightStrings(scale, width, height);
730   
731    /////
732    // DON'T TRY TO EXPORT IF VIEWER IS UNSUPPORTED
733  0 if (viewerType != ViewerType.JMOL)
734    {
735  0 addWarn("Cannot export image for structure viewer "
736    + viewerType.name() + " yet");
737  0 continue;
738    }
739   
740    /////
741    // Apply the temporary colourscheme to the linked alignment
742    // TODO: enhance for multiple linked alignments.
743   
744  0 String imageColour = avm.getValueFromSubValOrArg(
745    structureImageArgValue, Arg.IMAGECOLOUR,
746    structureImageSubVals);
747  0 ColourSchemeI originalColourScheme = this
748    .getColourScheme(af);
749  0 this.colourAlignFrame(af, imageColour);
750   
751    /////
752    // custom image background colour
753   
754  0 String bgcolourstring = avm.getValueFromSubValOrArg(
755    structureImageArgValue, Arg.BGCOLOUR,
756    structureImageSubVals);
757  0 Color bgcolour = null;
758  0 if (bgcolourstring != null && bgcolourstring.length() > 0)
759    {
760  0 bgcolour = ColorUtils.parseColourString(bgcolourstring);
761  0 if (bgcolour == null)
762    {
763  0 Console.warn(
764    "Background colour string '" + bgcolourstring
765    + "' not recognised -- using default");
766    }
767    }
768   
769  0 JalviewStructureDisplayI sview = structureViewer
770    .getJalviewStructureDisplay();
771   
772  0 File sessionToRestore = null;
773   
774  0 List<StructureCommandI> extraCommands = new ArrayList<>();
775   
776  0 if (extraCommands.size() > 0 || bgcolour != null)
777    {
778  0 try
779    {
780  0 sessionToRestore = sview.saveSession();
781    } catch (Throwable t)
782    {
783  0 Console.warn(
784    "Unable to save temporary session file before custom structure view export operation.");
785    }
786    }
787   
788    ////
789    // Do temporary ops
790   
791  0 if (bgcolour != null)
792    {
793  0 sview.getBinding().setBackgroundColour(bgcolour);
794    }
795   
796  0 sview.getBinding().executeCommands(extraCommands, false,
797    "Executing Custom Commands");
798   
799    // and export the view as an image
800  0 boolean success = this.checksBeforeWritingToFile(avm,
801    subVals, false, structureImageFilename,
802    "structure image", isError);
803   
804  0 if (!success)
805    {
806  0 continue;
807    }
808  0 Console.debug("Rendering image to " + structureImageFile);
809    //
810    // TODO - extend StructureViewer / Binding with makePDBImage so
811    // we can do this with every viewer
812    //
813   
814  0 try
815    {
816    // We don't expect class cast exception
817  0 AppJmol jmol = (AppJmol) sview;
818  0 jmol.makePDBImage(structureImageFile, imageType, renderer,
819    userBis);
820  0 Console.info("Exported structure image to "
821    + structureImageFile);
822   
823    // RESTORE SESSION AFTER EXPORT IF NEED BE
824  0 if (sessionToRestore != null)
825    {
826  0 Console.debug(
827    "Restoring session from " + sessionToRestore);
828   
829  0 sview.getBinding().restoreSession(
830    sessionToRestore.getAbsolutePath());
831   
832    }
833    } catch (ImageOutputException ioexec)
834    {
835  0 addError(
836    "Unexpected error when restoring structure viewer session after custom view operations.");
837  0 isError = true;
838  0 continue;
839    } finally
840    {
841  0 try
842    {
843  0 this.colourAlignFrame(af, originalColourScheme);
844    } catch (Exception t)
845    {
846  0 addError(
847    "Unexpected error when restoring colourscheme to alignment after temporary change for export.",
848    t);
849    }
850    }
851    }
852    }
853    }
854  0 argParser.setStructureFilename(null);
855    }
856    }
857    }
858   
859  72 if (af == null)
860    {
861  0 af = afMap.get(id);
862    }
863    // many of jalview's format/layout methods are only thread safe on the
864    // swingworker thread.
865    // all these methods should be on the alignViewController so it can
866    // coordinate such details
867  72 if (headless)
868    {
869  0 showOrHideAnnotations(af, showSSAnnotations, showAnnotations,
870    hideTFrows);
871    }
872    else
873    {
874  72 try
875    {
876  72 AlignFrame _af = af;
877  72 final boolean _showSSAnnotations = showSSAnnotations;
878  72 final boolean _showAnnotations = showAnnotations;
879  72 final boolean _hideTFrows = hideTFrows;
880  72 SwingUtilities.invokeAndWait(() -> {
881  72 showOrHideAnnotations(_af, _showSSAnnotations, _showAnnotations,
882    _hideTFrows);
883    }
884   
885    );
886    } catch (Exception x)
887    {
888  0 Console.warn(
889    "Unexpected exception adjusting annotation row visibility.",
890    x);
891    }
892    }
893   
894  72 if (wrap)
895    {
896  0 if (af == null)
897    {
898  0 af = afMap.get(id);
899    }
900  0 if (af != null)
901    {
902  0 af.setWrapFormat(wrap, true);
903    }
904    }
905   
906    /*
907    boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING);
908    if (doShading)
909    {
910    AlignFrame af = afMap.get(id);
911    for (AlignmentAnnotation aa : af.alignPanel.getAlignment()
912    .findAnnotation(PDBChain.class.getName().toString()))
913    {
914    AnnotationColourGradient acg = new AnnotationColourGradient(aa,
915    af.alignPanel.av.getGlobalColourScheme(), 0);
916    acg.setSeqAssociated(true);
917    af.changeColour(acg);
918    Console.info("Changed colour " + acg.toString());
919    }
920    }
921    */
922   
923  72 return theseArgsWereParsed && !isError;
924    }
925   
 
926  72 toggle private static void showOrHideAnnotations(AlignFrame af,
927    boolean showSSAnnotations, boolean showAnnotations,
928    boolean hideTFrows)
929    {
930  72 if (af == null)
931    {
932  0 Console.warn("Expected Alignment Window not opened");
933  0 return;
934    }
935  72 af.setAnnotationsVisibility(showSSAnnotations, true, false);
936  72 af.setAnnotationsVisibility(showAnnotations, false, true);
937   
938    // show temperature factor annotations?
939  72 if (hideTFrows)
940    {
941    // do this better (annotation types?)
942  0 List<String> hideThese = new ArrayList<>();
943  0 hideThese.add("Temperature Factor");
944  0 hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
945  0 AlignmentUtils.showOrHideSequenceAnnotations(
946    af.getCurrentView().getAlignment(), hideThese, null, false,
947    false);
948    }
949    }
950   
 
951  72 toggle protected void processGroovyScript(String id)
952    {
953  72 ArgValuesMap avm = argParser.getLinkedArgs(id);
954  72 AlignFrame af = afMap.get(id);
955   
956  72 if (avm != null && !avm.containsArg(Arg.GROOVY))
957    {
958    // nothing to do
959  72 return;
960    }
961   
962  0 if (af == null)
963    {
964  0 addWarn("Groovy script does not have an alignment window. Proceeding with caution!");
965    }
966   
967  0 if (avm.containsArg(Arg.GROOVY))
968    {
969  0 for (ArgValue groovyAv : avm.getArgValueList(Arg.GROOVY))
970    {
971  0 String groovyscript = groovyAv.getValue();
972  0 if (groovyscript != null)
973    {
974    // Execute the groovy script after we've done all the rendering stuff
975    // and before any images or figures are generated.
976  0 Console.info("Executing script " + groovyscript);
977  0 Jalview.getInstance().executeGroovyScript(groovyscript, af);
978    }
979    }
980    }
981    }
982   
 
983  72 toggle protected boolean processImages(String id)
984    {
985  72 ArgValuesMap avm = argParser.getLinkedArgs(id);
986  72 AlignFrame af = afMap.get(id);
987   
988  72 if (avm != null && !avm.containsArg(Arg.IMAGE))
989    {
990    // nothing to do
991  60 return true;
992    }
993   
994  12 if (af == null)
995    {
996  0 addWarn("Do not have an alignment window to create image from (id="
997    + id + "). Not proceeding.");
998  0 return false;
999    }
1000   
1001  12 Boolean isError = Boolean.valueOf(false);
1002  12 if (avm.containsArg(Arg.IMAGE))
1003    {
1004  12 for (ArgValue imageAv : avm.getArgValueList(Arg.IMAGE))
1005    {
1006  12 String val = imageAv.getValue();
1007  12 SubVals imageSubVals = imageAv.getSubVals();
1008  12 String fileName = imageSubVals.getContent();
1009  12 File file = new File(fileName);
1010  12 String name = af.getName();
1011  12 String renderer = avm.getValueFromSubValOrArg(imageAv,
1012    Arg.TEXTRENDERER, imageSubVals);
1013  12 if (renderer == null)
1014  12 renderer = "text";
1015  12 String type = "png"; // default
1016   
1017  12 String scale = avm.getValueFromSubValOrArg(imageAv, Arg.SCALE,
1018    imageSubVals);
1019  12 String width = avm.getValueFromSubValOrArg(imageAv, Arg.WIDTH,
1020    imageSubVals);
1021  12 String height = avm.getValueFromSubValOrArg(imageAv, Arg.HEIGHT,
1022    imageSubVals);
1023  12 BitmapImageSizing userBis = ImageMaker
1024    .parseScaleWidthHeightStrings(scale, width, height);
1025   
1026  12 type = avm.getValueFromSubValOrArg(imageAv, Arg.TYPE, imageSubVals);
1027  12 if (type == null && fileName != null)
1028    {
1029  12 for (String ext : new String[] { "svg", "png", "html", "eps" })
1030    {
1031  48 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1032    {
1033  12 type = ext;
1034    }
1035    }
1036    }
1037    // for moment we disable JSON export
1038  12 Cache.setPropsAreReadOnly(true);
1039  12 Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
1040   
1041  12 String imageColour = avm.getValueFromSubValOrArg(imageAv,
1042    Arg.IMAGECOLOUR, imageSubVals);
1043  12 ColourSchemeI originalColourScheme = this.getColourScheme(af);
1044  12 this.colourAlignFrame(af, imageColour);
1045   
1046  12 Console.info("Writing " + file);
1047   
1048  12 boolean success = checksBeforeWritingToFile(avm, imageSubVals,
1049    false, fileName, "image", isError);
1050  12 if (!success)
1051    {
1052  0 continue;
1053    }
1054   
1055  12 try
1056    {
1057  12 switch (type)
1058    {
1059   
1060  0 case "svg":
1061  0 Console.debug("Outputting type '" + type + "' to " + fileName);
1062  0 af.createSVG(file, renderer);
1063  0 break;
1064   
1065  12 case "png":
1066  12 Console.debug("Outputting type '" + type + "' to " + fileName);
1067  12 af.createPNG(file, null, userBis);
1068  12 break;
1069   
1070  0 case "html":
1071  0 Console.debug("Outputting type '" + type + "' to " + fileName);
1072  0 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1073  0 htmlSVG.exportHTML(fileName, renderer);
1074  0 break;
1075   
1076  0 case "biojs":
1077  0 Console.debug(
1078    "Outputting BioJS MSA Viwer HTML file: " + fileName);
1079  0 try
1080    {
1081  0 BioJsHTMLOutput.refreshVersionInfo(
1082    BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1083    } catch (URISyntaxException e)
1084    {
1085  0 e.printStackTrace();
1086    }
1087  0 BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1088  0 bjs.exportHTML(fileName);
1089  0 break;
1090   
1091  0 case "eps":
1092  0 Console.debug("Outputting EPS file: " + fileName);
1093  0 af.createEPS(file, renderer);
1094  0 break;
1095   
1096  0 case "imagemap":
1097  0 Console.debug("Outputting ImageMap file: " + fileName);
1098  0 af.createImageMap(file, name);
1099  0 break;
1100   
1101  0 default:
1102  0 addWarn(Arg.IMAGE.argString() + " type '" + type
1103    + "' not known. Ignoring");
1104  0 break;
1105    }
1106    } catch (Exception ioex)
1107    {
1108  0 addError("Unexpected error during export to '" + fileName + "'",
1109    ioex);
1110  0 isError = true;
1111    }
1112   
1113  12 this.colourAlignFrame(af, originalColourScheme);
1114    }
1115    }
1116  12 return !isError;
1117    }
1118   
 
1119  72 toggle protected boolean processOutput(String id)
1120    {
1121  72 ArgValuesMap avm = argParser.getLinkedArgs(id);
1122  72 AlignFrame af = afMap.get(id);
1123   
1124  72 if (avm != null && !avm.containsArg(Arg.OUTPUT))
1125    {
1126    // nothing to do
1127  18 return true;
1128    }
1129   
1130  54 if (af == null)
1131    {
1132  0 addWarn("Do not have an alignment window (id=" + id
1133    + "). Not proceeding.");
1134  0 return false;
1135    }
1136   
1137  54 Boolean isError = Boolean.valueOf(false);
1138   
1139  54 if (avm.containsArg(Arg.OUTPUT))
1140    {
1141  54 for (ArgValue av : avm.getArgValueList(Arg.OUTPUT))
1142    {
1143  54 String val = av.getValue();
1144  54 SubVals subVals = av.getSubVals();
1145  54 String fileName = subVals.getContent();
1146  54 boolean stdout = ArgParser.STDOUTFILENAME.equals(fileName);
1147  54 File file = new File(fileName);
1148   
1149  54 String name = af.getName();
1150  54 String format = avm.getValueFromSubValOrArg(av, Arg.FORMAT,
1151    subVals);
1152  54 FileFormats ffs = FileFormats.getInstance();
1153  54 List<String> validFormats = ffs.getWritableFormats(false);
1154   
1155  54 FileFormatI ff = null;
1156  54 if (format == null && fileName != null)
1157    {
1158  54 FORMAT: for (String fname : validFormats)
1159    {
1160  204 FileFormatI tff = ffs.forName(fname);
1161  204 String[] extensions = tff.getExtensions().split(",");
1162  204 for (String ext : extensions)
1163    {
1164  420 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1165    {
1166  54 ff = tff;
1167  54 format = ff.getName();
1168  54 break FORMAT;
1169    }
1170    }
1171    }
1172    }
1173  54 if (ff == null && format != null)
1174    {
1175  0 ff = ffs.forName(format);
1176    }
1177  54 if (ff == null)
1178    {
1179  0 if (stdout)
1180    {
1181  0 ff = FileFormat.Fasta;
1182    }
1183    else
1184    {
1185  0 StringBuilder validSB = new StringBuilder();
1186  0 for (String f : validFormats)
1187    {
1188  0 if (validSB.length() > 0)
1189  0 validSB.append(", ");
1190  0 validSB.append(f);
1191  0 FileFormatI tff = ffs.forName(f);
1192  0 validSB.append(" (");
1193  0 validSB.append(tff.getExtensions());
1194  0 validSB.append(")");
1195    }
1196   
1197  0 addError("No valid format specified for "
1198    + Arg.OUTPUT.argString() + ". Valid formats are "
1199    + validSB.toString() + ".");
1200  0 continue;
1201    }
1202    }
1203   
1204  54 boolean success = checksBeforeWritingToFile(avm, subVals, true,
1205    fileName, ff.getName(), isError);
1206  54 if (!success)
1207    {
1208  0 continue;
1209    }
1210   
1211  54 boolean backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVals,
1212  54 null, Platform.isHeadless() ? null : BackupFiles.ENABLED,
1213    !Platform.isHeadless());
1214   
1215  54 Console.info("Writing " + fileName);
1216   
1217  54 af.saveAlignment(fileName, ff, stdout, backups);
1218  54 if (af.isSaveAlignmentSuccessful())
1219    {
1220  54 Console.debug("Written alignment '" + name + "' in "
1221    + ff.getName() + " format to '" + file + "'");
1222    }
1223    else
1224    {
1225  0 addError("Error writing file '" + file + "' in " + ff.getName()
1226    + " format!");
1227  0 isError = true;
1228  0 continue;
1229    }
1230   
1231    }
1232    }
1233  54 return !isError;
1234    }
1235   
 
1236  0 toggle private SequenceI getSpecifiedSequence(AlignFrame af, ArgValuesMap avm,
1237    ArgValue av)
1238    {
1239  0 SubVals subVals = av.getSubVals();
1240  0 ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID, true);
1241  0 SequenceI seq = null;
1242  0 if (subVals == null && idAv == null)
1243  0 return null;
1244  0 if (af == null || af.getCurrentView() == null)
1245    {
1246  0 return null;
1247    }
1248  0 AlignmentI al = af.getCurrentView().getAlignment();
1249  0 if (al == null)
1250    {
1251  0 return null;
1252    }
1253  0 if (subVals != null)
1254    {
1255  0 if (subVals.has(Arg.SEQID.getName()))
1256    {
1257  0 seq = al.findName(subVals.get(Arg.SEQID.getName()));
1258    }
1259  0 else if (-1 < subVals.getIndex()
1260    && subVals.getIndex() < al.getSequences().size())
1261    {
1262  0 seq = al.getSequenceAt(subVals.getIndex());
1263    }
1264    }
1265  0 if (seq == null && idAv != null)
1266    {
1267  0 seq = al.findName(idAv.getValue());
1268    }
1269  0 return seq;
1270    }
1271   
 
1272  0 toggle public AlignFrame[] getAlignFrames()
1273    {
1274  0 AlignFrame[] afs = null;
1275  0 if (afMap != null)
1276    {
1277  0 afs = (AlignFrame[]) afMap.values().toArray();
1278    }
1279   
1280  0 return afs;
1281    }
1282   
 
1283  0 toggle public List<StructureViewer> getStructureViewers()
1284    {
1285  0 List<StructureViewer> svs = null;
1286  0 if (svMap != null)
1287    {
1288  0 for (List<StructureViewer> svList : svMap.values())
1289    {
1290  0 if (svs == null)
1291    {
1292  0 svs = new ArrayList<>();
1293    }
1294  0 svs.addAll(svList);
1295    }
1296    }
1297  0 return svs;
1298    }
1299   
 
1300  12 toggle private void colourAlignFrame(AlignFrame af, String colour)
1301    {
1302    // use string "none" to remove colour scheme
1303  12 if (colour != null && "" != colour)
1304    {
1305  0 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
1306    af.getViewport(), af.getViewport().getAlignment(), colour);
1307  0 if (cs == null && !StringUtils.equalsIgnoreCase(colour, "none"))
1308    {
1309  0 addWarn("Couldn't parse '" + colour + "' as a colourscheme.");
1310    }
1311    else
1312    {
1313  0 Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
1314  0 colourAlignFrame(af, cs);
1315    }
1316    }
1317    }
1318   
 
1319  12 toggle private void colourAlignFrame(AlignFrame af, ColourSchemeI cs)
1320    {
1321  12 try
1322    {
1323  12 SwingUtilities.invokeAndWait(new Runnable()
1324    {
 
1325  12 toggle @Override
1326    public void run()
1327    {
1328    // Note that cs == null removes colour scheme from af
1329  12 af.changeColour(cs);
1330    }
1331    });
1332    } catch (Exception x)
1333    {
1334  0 Console.trace("Interrupted whilst waiting for colorAlignFrame action",
1335    x);
1336   
1337    }
1338    }
1339   
 
1340  12 toggle private ColourSchemeI getColourScheme(AlignFrame af)
1341    {
1342  12 return af.getViewport().getGlobalColourScheme();
1343    }
1344   
 
1345  0 toggle private void addInfo(String errorMessage)
1346    {
1347  0 Console.info(errorMessage);
1348  0 errors.add(errorMessage);
1349    }
1350   
 
1351  0 toggle private void addWarn(String errorMessage)
1352    {
1353  0 Console.warn(errorMessage);
1354  0 errors.add(errorMessage);
1355    }
1356   
 
1357  0 toggle private void addError(String errorMessage)
1358    {
1359  0 addError(errorMessage, null);
1360    }
1361   
 
1362  0 toggle private void addError(String errorMessage, Exception e)
1363    {
1364  0 Console.error(errorMessage, e);
1365  0 errors.add(errorMessage);
1366    }
1367   
 
1368  66 toggle private boolean checksBeforeWritingToFile(ArgValuesMap avm,
1369    SubVals subVal, boolean includeBackups, String filename,
1370    String adjective, Boolean isError)
1371    {
1372  66 File file = new File(filename);
1373   
1374  66 boolean overwrite = avm.getFromSubValArgOrPref(Arg.OVERWRITE, subVal,
1375    null, "OVERWRITE_OUTPUT", false);
1376  66 boolean stdout = false;
1377  66 boolean backups = false;
1378  66 if (includeBackups)
1379    {
1380  54 stdout = ArgParser.STDOUTFILENAME.equals(filename);
1381    // backups. Use the Arg.BACKUPS or subval "backups" setting first,
1382    // otherwise if headless assume false, if not headless use the user
1383    // preference with default true.
1384  54 backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVal, null,
1385  54 Platform.isHeadless() ? null : BackupFiles.ENABLED,
1386    !Platform.isHeadless());
1387    }
1388   
1389  66 if (file.exists() && !(overwrite || backups || stdout))
1390    {
1391  0 addWarn("Won't overwrite file '" + filename + "' without "
1392    + Arg.OVERWRITE.argString()
1393  0 + (includeBackups ? " or " + Arg.BACKUPS.argString() : "")
1394    + " set");
1395  0 return false;
1396    }
1397   
1398  66 boolean mkdirs = avm.getFromSubValArgOrPref(Arg.MKDIRS, subVal, null,
1399    "MKDIRS_OUTPUT", false);
1400   
1401  66 if (!FileUtils.checkParentDir(file, mkdirs))
1402    {
1403  0 addError("Directory '"
1404    + FileUtils.getParentDir(file).getAbsolutePath()
1405    + "' does not exist for " + adjective + " file '" + filename
1406    + "'."
1407  0 + (mkdirs ? "" : " Try using " + Arg.MKDIRS.argString()));
1408  0 isError = true;
1409  0 return false;
1410    }
1411   
1412  66 return true;
1413    }
1414   
 
1415  43 toggle public List<String> getErrors()
1416    {
1417  43 return errors;
1418    }
1419   
 
1420  43 toggle public String errorsToString()
1421    {
1422  43 StringBuilder sb = new StringBuilder();
1423  43 for (String error : errors)
1424    {
1425  0 if (sb.length() > 0)
1426  0 sb.append("\n");
1427  0 sb.append("- " + error);
1428    }
1429  43 return sb.toString();
1430    }
1431    }