Clover icon

Coverage Report

  1. Project Clover database Mon Nov 11 2024 17:27:16 GMT
  2. Package jalview.bin

File Commands.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% 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.6733668467.3%
 

Contributing tests

This file is covered by 57 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  129 toggle public Commands(ArgParser argparser, boolean headless)
105    {
106  129 this(Desktop.instance, argparser, headless);
107    }
108   
 
109  129 toggle public Commands(Desktop d, ArgParser argparser, boolean h)
110    {
111  129 argParser = argparser;
112  129 headless = h;
113  129 desktop = d;
114  129 afMap = new HashMap<>();
115    }
116   
 
117  129 toggle protected boolean processArgs()
118    {
119  129 if (argParser == null)
120    {
121  0 return true;
122    }
123   
124  129 boolean theseArgsWereParsed = false;
125   
126  129 if (argParser != null && argParser.getLinkedIds() != null)
127    {
128  129 for (String id : argParser.getLinkedIds())
129    {
130  155 ArgValuesMap avm = argParser.getLinkedArgs(id);
131  155 theseArgsWereParsed = true;
132  155 boolean processLinkedOkay = processLinked(id);
133  151 theseArgsWereParsed &= processLinkedOkay;
134   
135  151 processGroovyScript(id);
136   
137    // wait around until alignFrame isn't busy
138  151 AlignFrame af = afMap.get(id);
139  759 while (af != null && af.getViewport().isCalcInProgress())
140    {
141  608 try
142    {
143  608 Thread.sleep(25);
144    } catch (Exception q)
145    {
146    }
147  608 ;
148    }
149   
150  151 theseArgsWereParsed &= processImages(id);
151   
152  151 if (processLinkedOkay)
153    {
154  151 theseArgsWereParsed &= processOutput(id);
155    }
156   
157    // close ap
158  151 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  125 String errorsRaised = errorsToString();
173  125 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  125 if (argParser.getBoolean(Arg.QUIT))
182    {
183  1 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  124 argsWereParsed = theseArgsWereParsed;
189  124 return argsWereParsed;
190    }
191   
 
192  13 toggle public boolean commandArgsProvided()
193    {
194  13 return commandArgsProvided;
195    }
196   
 
197  136 toggle public boolean argsWereParsed()
198    {
199  136 return argsWereParsed;
200    }
201   
 
202  155 toggle protected boolean processLinked(String id)
203    {
204  155 boolean theseArgsWereParsed = false;
205  155 ArgValuesMap avm = argParser.getLinkedArgs(id);
206  155 if (avm == null)
207    {
208  0 return true;
209    }
210   
211  155 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  155 boolean wrap = false;
216  155 boolean showSSAnnotations = false;
217  155 boolean showAnnotations = false;
218  155 boolean hideTFrows = false;
219  155 AlignFrame af = null;
220   
221  155 if (avm.containsArg(Arg.APPEND) || avm.containsArg(Arg.OPEN))
222    {
223  155 commandArgsProvided = true;
224  155 final long progress = System.currentTimeMillis();
225   
226  155 boolean first = true;
227  155 boolean progressBarSet = false;
228    // Combine the APPEND and OPEN files into one list, along with whether it
229    // was APPEND or OPEN
230  155 List<ArgValue> openAvList = new ArrayList<>();
231  155 openAvList.addAll(avm.getArgValueList(Arg.OPEN));
232  155 openAvList.addAll(avm.getArgValueList(Arg.APPEND));
233    // sort avlist based on av.getArgIndex()
234  155 Collections.sort(openAvList);
235  155 for (ArgValue av : openAvList)
236    {
237  176 Arg a = av.getArg();
238  176 SubVals sv = av.getSubVals();
239  176 String openFile0 = av.getValue();
240  176 String openFile = HttpUtils.equivalentJalviewUrl(openFile0);
241  176 if (openFile == null)
242  0 continue;
243   
244  176 theseArgsWereParsed = true;
245  176 if (first)
246    {
247  155 first = false;
248  155 if (!headless && desktop != null)
249    {
250  117 SwingUtilities.invokeLater(new Runnable()
251    {
 
252  116 toggle @Override
253    public void run()
254    {
255  116 desktop.setProgressBar(
256    MessageManager.getString(
257    "status.processing_commandline_args"),
258    progress);
259   
260    }
261    });
262  117 progressBarSet = true;
263    }
264    }
265   
266  176 if (!Platform.isJS())
267    /**
268    * ignore in JavaScript -- can't just file existence - could load it?
269    *
270    * @j2sIgnore
271    */
272    {
273  176 if (!HttpUtils.isPlausibleUri(openFile))
274    {
275  176 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  176 DataSourceType protocol = AppletFormatAdapter
285    .checkProtocol(openFile);
286   
287  176 FileFormatI format = null;
288  176 try
289    {
290  176 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  176 af = afMap.get(id);
305    // When to open a new AlignFrame
306  176 if (af == null || "true".equals(av.getSubVal("new"))
307    || a == Arg.OPEN || format == FileFormat.Jalview)
308    {
309  156 if (a == Arg.OPEN)
310    {
311  133 Jalview.testoutput(argParser, Arg.OPEN, "examples/uniref50.fa",
312    openFile);
313    }
314   
315  156 Console.debug(
316    "Opening '" + openFile + "' in new alignment frame");
317  156 FileLoader fileLoader = new FileLoader(!headless);
318  156 boolean xception = false;
319  156 try
320    {
321  156 af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol,
322    format);
323  152 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  152 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  152 String colour = null;
347  152 if (avm.containsArg(Arg.COLOUR)
348    || !(format == FileFormat.Jalview))
349    {
350  152 colour = avm.getFromSubValArgOrPref(av, Arg.COLOUR, sv, null,
351    "DEFAULT_COLOUR_PROT", null);
352    }
353  152 if (colour != null)
354    {
355  13 this.colourAlignFrame(af, colour);
356    }
357   
358    // Change alignment frame title
359  152 String title = avm.getFromSubValArgOrPref(av, Arg.TITLE, sv, null,
360    null, null);
361  152 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  152 String featuresfile = avm.getValueFromSubValOrArg(av,
369    Arg.FEATURES, sv);
370  152 if (featuresfile != null)
371    {
372  1 af.parseFeaturesFile(featuresfile,
373    AppletFormatAdapter.checkProtocol(featuresfile));
374  1 Jalview.testoutput(argParser, Arg.FEATURES,
375    "examples/testdata/plantfdx.features", featuresfile);
376    }
377   
378    // Add annotations from file
379  152 String annotationsfile = avm.getValueFromSubValOrArg(av,
380    Arg.ANNOTATIONS, sv);
381  152 if (annotationsfile != null)
382    {
383  1 af.loadJalviewDataFile(annotationsfile, null, null, null);
384  1 Jalview.testoutput(argParser, Arg.ANNOTATIONS,
385    "examples/testdata/plantfdx.annotations",
386    annotationsfile);
387    }
388   
389    // Set or clear the sortbytree flag
390  152 boolean sortbytree = avm.getBoolFromSubValOrArg(Arg.SORTBYTREE,
391    sv);
392  152 if (sortbytree)
393    {
394  1 af.getViewport().setSortByTree(true);
395  1 Jalview.testoutput(argParser, Arg.SORTBYTREE);
396    }
397   
398    // Load tree from file
399  152 String treefile = avm.getValueFromSubValOrArg(av, Arg.TREE, sv);
400  152 if (treefile != null)
401    {
402  1 try
403    {
404  1 NewickFile nf = new NewickFile(treefile,
405    AppletFormatAdapter.checkProtocol(treefile));
406  1 af.getViewport().setCurrentTree(
407    af.showNewickTree(nf, treefile).getTree());
408  1 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  152 showSSAnnotations = avm.getFromSubValArgOrPref(
419    Arg.SHOWSSANNOTATIONS, av.getSubVals(), null,
420    "STRUCT_FROM_PDB", true);
421    // Show sequence annotations?
422  152 showAnnotations = avm.getFromSubValArgOrPref(Arg.SHOWANNOTATIONS,
423    av.getSubVals(), null, "SHOW_ANNOTATIONS", true);
424    // hide the Temperature Factor row?
425  152 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  152 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  152 afMap.put(id, af);
438   
439    // is it its own structure file?
440  152 if (format.isStructureFile())
441    {
442  2 StructureSelectionManager ssm = StructureSelectionManager
443    .getStructureSelectionManager(Desktop.instance);
444  2 SequenceI seq = af.alignPanel.getAlignment().getSequenceAt(0);
445  2 ssm.computeMapping(false, new SequenceI[] { seq }, null,
446    openFile, DataSourceType.FILE, null, null, null, false);
447    }
448    }
449    else
450    {
451  20 Console.debug(
452    "Opening '" + openFile + "' in existing alignment frame");
453   
454  20 DataSourceType dst = HttpUtils.startsWithHttpOrHttps(openFile)
455    ? DataSourceType.URL
456    : DataSourceType.FILE;
457   
458  20 FileLoader fileLoader = new FileLoader(!headless);
459  20 fileLoader.LoadFile(af.getCurrentView(), openFile, dst, null,
460    false);
461    }
462   
463  172 Console.debug("Command " + Arg.APPEND + " executed successfully!");
464   
465    }
466  151 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  151 if (progressBarSet && desktop != null)
479  113 desktop.setProgressBar(null, progress);
480   
481    }
482   
483    // open the structure (from same PDB file or given PDBfile)
484  151 if (!avm.getBoolean(Arg.NOSTRUCTURE))
485    {
486  151 if (af == null)
487    {
488  0 af = afMap.get(id);
489    }
490  151 if (avm.containsArg(Arg.STRUCTURE))
491    {
492  28 commandArgsProvided = true;
493  28 for (ArgValue structureAv : avm.getArgValueList(Arg.STRUCTURE))
494    {
495  42 argParser.setStructureFilename(null);
496  42 String val = structureAv.getValue();
497  42 SubVals subVals = structureAv.getSubVals();
498  42 int argIndex = structureAv.getArgIndex();
499  42 SequenceI seq = getSpecifiedSequence(af, avm, structureAv);
500  42 if (seq == null)
501    {
502    // Could not find sequence from subId, let's assume the first
503    // sequence in the alignframe
504  26 AlignmentI al = af.getCurrentView().getAlignment();
505  26 seq = al.getSequenceAt(0);
506    }
507   
508  42 if (seq == null)
509    {
510  0 addWarn("Could not find sequence for argument "
511    + Arg.STRUCTURE.argString() + "=" + val);
512  0 continue;
513    }
514  42 String structureFilename = null;
515  42 File structureFile = null;
516  42 if (subVals.getContent() != null
517    && subVals.getContent().length() != 0)
518    {
519  42 structureFilename = subVals.getContent();
520  42 Console.debug("Using structure file (from argument) '"
521    + structureFilename + "'");
522  42 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  42 if (structureFilename == null || structureFile == null)
539    {
540  0 addWarn("Not provided structure file with '" + val + "'");
541  0 continue;
542    }
543   
544  42 if (!structureFile.exists())
545    {
546  0 addWarn("Structure file '" + structureFile.getAbsoluteFile()
547    + "' not found.");
548  0 continue;
549    }
550   
551  42 Console.debug("Using structure file "
552    + structureFile.getAbsolutePath());
553   
554  42 argParser.setStructureFilename(structureFilename);
555   
556    // open structure view
557  42 AlignmentPanel ap = af.alignPanel;
558  42 if (headless)
559    {
560  10 Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
561    StructureViewer.ViewerType.JMOL.toString());
562    }
563   
564  42 String structureFilepath = structureFile.getAbsolutePath();
565   
566    // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
567  42 String paeFilepath = avm.getFromSubValArgOrPrefWithSubstitutions(
568    argParser, Arg.PAEMATRIX, ArgValuesMap.Position.AFTER,
569    structureAv, subVals, null, null, null);
570  42 if (paeFilepath != null)
571    {
572  26 File paeFile = new File(paeFilepath);
573   
574  26 try
575    {
576  26 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  42 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  42 String tftString = avm.getFromSubValArgOrPrefWithSubstitutions(
593    argParser, Arg.TEMPFAC, ArgValuesMap.Position.AFTER,
594    structureAv, subVals, null, null, null);
595  42 boolean notempfac = avm.getFromSubValArgOrPref(Arg.NOTEMPFAC,
596    subVals, null, "ADD_TEMPFACT_ANN", false, true);
597  42 TFType tft = notempfac ? null : TFType.DEFAULT;
598  42 if (tftString != null && !notempfac)
599    {
600    // get kind of temperature factor annotation
601  12 try
602    {
603  12 tft = TFType.valueOf(tftString.toUpperCase(Locale.ROOT));
604  12 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  42 String sViewerName = avm.getFromSubValArgOrPref(
626    Arg.STRUCTUREVIEWER, ArgValuesMap.Position.AFTER,
627    structureAv, subVals, null, null, "jmol");
628  42 ViewerType viewerType = ViewerType.getFromString(sViewerName);
629   
630    // TODO use ssFromStructure
631  42 StructureViewer structureViewer = StructureChooser
632    .openStructureFileForSequence(null, null, ap, seq, false,
633    structureFilepath, tft, paeFilepath, false,
634    ssFromStructure, false, viewerType);
635   
636  42 if (structureViewer == null)
637    {
638  9 if (!StringUtils.equalsIgnoreCase(sViewerName, "none"))
639    {
640  0 addError("Failed to import and open structure view for file '"
641    + structureFile + "'.");
642    }
643  9 continue;
644    }
645  33 try
646    {
647  33 long tries = 1000;
648  303 while (structureViewer.isBusy() && tries > 0)
649    {
650  270 Thread.sleep(25);
651  270 if (structureViewer.isBusy())
652    {
653  237 tries--;
654  237 Console.debug(
655    "Waiting for viewer for " + structureFilepath);
656    }
657    }
658  33 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  33 if (svMap == null)
673    {
674  23 svMap = new HashMap<>();
675    }
676  33 if (svMap.get(id) == null)
677    {
678  27 svMap.put(id, new ArrayList<>());
679    }
680  33 svMap.get(id).add(structureViewer);
681   
682  33 Console.debug(
683    "Successfully opened viewer for " + structureFilepath);
684   
685  33 if (avm.containsArg(Arg.STRUCTUREIMAGE))
686    {
687  10 for (ArgValue structureImageArgValue : avm
688    .getArgValueListFromSubValOrArg(structureAv,
689    Arg.STRUCTUREIMAGE, subVals))
690    {
691  10 String structureImageFilename = argParser.makeSubstitutions(
692    structureImageArgValue.getValue(), id, true);
693  10 if (structureViewer != null && structureImageFilename != null)
694    {
695  10 SubVals structureImageSubVals = null;
696  10 structureImageSubVals = structureImageArgValue.getSubVals();
697  10 File structureImageFile = new File(structureImageFilename);
698  10 String width = avm.getValueFromSubValOrArg(
699    structureImageArgValue, Arg.WIDTH,
700    structureImageSubVals);
701  10 String height = avm.getValueFromSubValOrArg(
702    structureImageArgValue, Arg.HEIGHT,
703    structureImageSubVals);
704  10 String scale = avm.getValueFromSubValOrArg(
705    structureImageArgValue, Arg.SCALE,
706    structureImageSubVals);
707  10 String renderer = avm.getValueFromSubValOrArg(
708    structureImageArgValue, Arg.TEXTRENDERER,
709    structureImageSubVals);
710  10 String typeS = avm.getValueFromSubValOrArg(
711    structureImageArgValue, Arg.TYPE,
712    structureImageSubVals);
713  10 if (typeS == null || typeS.length() == 0)
714    {
715  10 typeS = FileUtils.getExtension(structureImageFile);
716    }
717  10 TYPE imageType;
718  10 try
719    {
720  10 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  10 BitmapImageSizing userBis = ImageMaker
729    .parseScaleWidthHeightStrings(scale, width, height);
730   
731    /////
732    // DON'T TRY TO EXPORT IF VIEWER IS UNSUPPORTED
733  10 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  10 String imageColour = avm.getValueFromSubValOrArg(
745    structureImageArgValue, Arg.IMAGECOLOUR,
746    structureImageSubVals);
747  10 ColourSchemeI originalColourScheme = this
748    .getColourScheme(af);
749  10 this.colourAlignFrame(af, imageColour);
750   
751    /////
752    // custom image background colour
753   
754  10 String bgcolourstring = avm.getValueFromSubValOrArg(
755    structureImageArgValue, Arg.BGCOLOUR,
756    structureImageSubVals);
757  10 Color bgcolour = null;
758  10 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  10 JalviewStructureDisplayI sview = structureViewer
770    .getJalviewStructureDisplay();
771   
772  10 File sessionToRestore = null;
773   
774  10 List<StructureCommandI> extraCommands = new ArrayList<>();
775   
776  10 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  10 if (bgcolour != null)
792    {
793  0 sview.getBinding().setBackgroundColour(bgcolour);
794    }
795   
796  10 sview.getBinding().executeCommands(extraCommands, false,
797    "Executing Custom Commands");
798   
799    // and export the view as an image
800  10 boolean success = this.checksBeforeWritingToFile(avm,
801    subVals, false, structureImageFilename,
802    "structure image", isError);
803   
804  10 if (!success)
805    {
806  0 continue;
807    }
808  10 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  10 try
815    {
816    // We don't expect class cast exception
817  10 AppJmol jmol = (AppJmol) sview;
818  10 jmol.makePDBImage(structureImageFile, imageType, renderer,
819    userBis);
820  10 Console.info("Exported structure image to "
821    + structureImageFile);
822   
823    // RESTORE SESSION AFTER EXPORT IF NEED BE
824  10 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  10 try
842    {
843  10 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  33 argParser.setStructureFilename(null);
855    }
856    }
857    }
858   
859  151 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  151 if (headless)
868    {
869  38 showOrHideAnnotations(af, showSSAnnotations, showAnnotations,
870    hideTFrows);
871    }
872    else
873    {
874  113 try
875    {
876  113 AlignFrame _af = af;
877  113 final boolean _showSSAnnotations = showSSAnnotations;
878  113 final boolean _showAnnotations = showAnnotations;
879  113 final boolean _hideTFrows = hideTFrows;
880  113 SwingUtilities.invokeAndWait(() -> {
881  113 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  151 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  151 return theseArgsWereParsed && !isError;
924    }
925   
 
926  151 toggle private static void showOrHideAnnotations(AlignFrame af,
927    boolean showSSAnnotations, boolean showAnnotations,
928    boolean hideTFrows)
929    {
930  151 if (af == null)
931    {
932  0 Console.warn("Expected Alignment Window not opened");
933  0 return;
934    }
935  151 af.setAnnotationsVisibility(showSSAnnotations, true, false);
936  151 af.setAnnotationsVisibility(showAnnotations, false, true);
937   
938    // show temperature factor annotations?
939  151 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  151 toggle protected void processGroovyScript(String id)
952    {
953  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
954  151 AlignFrame af = afMap.get(id);
955   
956  151 if (avm != null && !avm.containsArg(Arg.GROOVY))
957    {
958    // nothing to do
959  151 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  151 toggle protected boolean processImages(String id)
984    {
985  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
986  151 AlignFrame af = afMap.get(id);
987   
988  151 if (avm != null && !avm.containsArg(Arg.IMAGE))
989    {
990    // nothing to do
991  121 return true;
992    }
993   
994  30 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  30 Boolean isError = Boolean.valueOf(false);
1002  30 if (avm.containsArg(Arg.IMAGE))
1003    {
1004  30 for (ArgValue imageAv : avm.getArgValueList(Arg.IMAGE))
1005    {
1006  30 String val = imageAv.getValue();
1007  30 SubVals imageSubVals = imageAv.getSubVals();
1008  30 String fileName = imageSubVals.getContent();
1009  30 File file = new File(fileName);
1010  30 String name = af.getName();
1011  30 String renderer = avm.getValueFromSubValOrArg(imageAv,
1012    Arg.TEXTRENDERER, imageSubVals);
1013  30 if (renderer == null)
1014  30 renderer = "text";
1015  30 String type = "png"; // default
1016   
1017  30 String scale = avm.getValueFromSubValOrArg(imageAv, Arg.SCALE,
1018    imageSubVals);
1019  30 String width = avm.getValueFromSubValOrArg(imageAv, Arg.WIDTH,
1020    imageSubVals);
1021  30 String height = avm.getValueFromSubValOrArg(imageAv, Arg.HEIGHT,
1022    imageSubVals);
1023  30 BitmapImageSizing userBis = ImageMaker
1024    .parseScaleWidthHeightStrings(scale, width, height);
1025   
1026  30 type = avm.getValueFromSubValOrArg(imageAv, Arg.TYPE, imageSubVals);
1027  30 if (type == null && fileName != null)
1028    {
1029  30 for (String ext : new String[] { "svg", "png", "html", "eps" })
1030    {
1031  120 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1032    {
1033  30 type = ext;
1034    }
1035    }
1036    }
1037    // for moment we disable JSON export
1038  30 Cache.setPropsAreReadOnly(true);
1039  30 Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
1040   
1041  30 String imageColour = avm.getValueFromSubValOrArg(imageAv,
1042    Arg.IMAGECOLOUR, imageSubVals);
1043  30 ColourSchemeI originalColourScheme = this.getColourScheme(af);
1044  30 this.colourAlignFrame(af, imageColour);
1045   
1046  30 Console.info("Writing " + file);
1047   
1048  30 boolean success = checksBeforeWritingToFile(avm, imageSubVals,
1049    false, fileName, "image", isError);
1050  30 if (!success)
1051    {
1052  0 continue;
1053    }
1054   
1055  30 try
1056    {
1057  30 switch (type)
1058    {
1059   
1060  1 case "svg":
1061  1 Console.debug("Outputting type '" + type + "' to " + fileName);
1062  1 af.createSVG(file, renderer);
1063  1 break;
1064   
1065  23 case "png":
1066  23 Console.debug("Outputting type '" + type + "' to " + fileName);
1067  23 af.createPNG(file, null, userBis);
1068  23 break;
1069   
1070  1 case "html":
1071  1 Console.debug("Outputting type '" + type + "' to " + fileName);
1072  1 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1073  1 htmlSVG.exportHTML(fileName, renderer);
1074  1 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  5 case "eps":
1092  5 Console.debug("Outputting EPS file: " + fileName);
1093  5 af.createEPS(file, renderer);
1094  5 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  30 this.colourAlignFrame(af, originalColourScheme);
1114    }
1115    }
1116  30 return !isError;
1117    }
1118   
 
1119  151 toggle protected boolean processOutput(String id)
1120    {
1121  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
1122  151 AlignFrame af = afMap.get(id);
1123   
1124  151 if (avm != null && !avm.containsArg(Arg.OUTPUT))
1125    {
1126    // nothing to do
1127  77 return true;
1128    }
1129   
1130  74 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  74 Boolean isError = Boolean.valueOf(false);
1138   
1139  74 if (avm.containsArg(Arg.OUTPUT))
1140    {
1141  74 for (ArgValue av : avm.getArgValueList(Arg.OUTPUT))
1142    {
1143  74 String val = av.getValue();
1144  74 SubVals subVals = av.getSubVals();
1145  74 String fileName = subVals.getContent();
1146  74 boolean stdout = ArgParser.STDOUTFILENAME.equals(fileName);
1147  74 File file = new File(fileName);
1148   
1149  74 String name = af.getName();
1150  74 String format = avm.getValueFromSubValOrArg(av, Arg.FORMAT,
1151    subVals);
1152  74 FileFormats ffs = FileFormats.getInstance();
1153  74 List<String> validFormats = ffs.getWritableFormats(false);
1154   
1155  74 FileFormatI ff = null;
1156  74 if (format == null && fileName != null)
1157    {
1158  71 FORMAT: for (String fname : validFormats)
1159    {
1160  365 FileFormatI tff = ffs.forName(fname);
1161  365 String[] extensions = tff.getExtensions().split(",");
1162  365 for (String ext : extensions)
1163    {
1164  655 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1165    {
1166  62 ff = tff;
1167  62 format = ff.getName();
1168  62 break FORMAT;
1169    }
1170    }
1171    }
1172    }
1173  74 if (ff == null && format != null)
1174    {
1175  3 ff = ffs.forName(format);
1176    }
1177  74 if (ff == null)
1178    {
1179  9 if (stdout)
1180    {
1181  9 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  74 boolean success = checksBeforeWritingToFile(avm, subVals, true,
1205    fileName, ff.getName(), isError);
1206  74 if (!success)
1207    {
1208  0 continue;
1209    }
1210   
1211  74 boolean backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVals,
1212  74 null, Platform.isHeadless() ? null : BackupFiles.ENABLED,
1213    !Platform.isHeadless());
1214   
1215  74 Console.info("Writing " + fileName);
1216   
1217  74 af.saveAlignment(fileName, ff, stdout, backups);
1218  74 if (af.isSaveAlignmentSuccessful())
1219    {
1220  74 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  74 return !isError;
1234    }
1235   
 
1236  42 toggle private SequenceI getSpecifiedSequence(AlignFrame af, ArgValuesMap avm,
1237    ArgValue av)
1238    {
1239  42 SubVals subVals = av.getSubVals();
1240  42 ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID, true);
1241  42 SequenceI seq = null;
1242  42 if (subVals == null && idAv == null)
1243  0 return null;
1244  42 if (af == null || af.getCurrentView() == null)
1245    {
1246  0 return null;
1247    }
1248  42 AlignmentI al = af.getCurrentView().getAlignment();
1249  42 if (al == null)
1250    {
1251  0 return null;
1252    }
1253  42 if (subVals != null)
1254    {
1255  42 if (subVals.has(Arg.SEQID.getName()))
1256    {
1257  8 seq = al.findName(subVals.get(Arg.SEQID.getName()));
1258    }
1259  34 else if (-1 < subVals.getIndex()
1260    && subVals.getIndex() < al.getSequences().size())
1261    {
1262  0 seq = al.getSequenceAt(subVals.getIndex());
1263    }
1264    }
1265  42 if (seq == null && idAv != null)
1266    {
1267  12 seq = al.findName(idAv.getValue());
1268    }
1269  42 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  53 toggle private void colourAlignFrame(AlignFrame af, String colour)
1301    {
1302    // use string "none" to remove colour scheme
1303  53 if (colour != null && "" != colour)
1304    {
1305  13 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
1306    af.getViewport(), af.getViewport().getAlignment(), colour);
1307  13 if (cs == null && !StringUtils.equalsIgnoreCase(colour, "none"))
1308    {
1309  0 addWarn("Couldn't parse '" + colour + "' as a colourscheme.");
1310    }
1311    else
1312    {
1313  13 Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
1314  13 colourAlignFrame(af, cs);
1315    }
1316    }
1317    }
1318   
 
1319  53 toggle private void colourAlignFrame(AlignFrame af, ColourSchemeI cs)
1320    {
1321  53 try
1322    {
1323  53 SwingUtilities.invokeAndWait(new Runnable()
1324    {
 
1325  53 toggle @Override
1326    public void run()
1327    {
1328    // Note that cs == null removes colour scheme from af
1329  53 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  40 toggle private ColourSchemeI getColourScheme(AlignFrame af)
1341    {
1342  40 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  114 toggle private boolean checksBeforeWritingToFile(ArgValuesMap avm,
1369    SubVals subVal, boolean includeBackups, String filename,
1370    String adjective, Boolean isError)
1371    {
1372  114 File file = new File(filename);
1373   
1374  114 boolean overwrite = avm.getFromSubValArgOrPref(Arg.OVERWRITE, subVal,
1375    null, "OVERWRITE_OUTPUT", false);
1376  114 boolean stdout = false;
1377  114 boolean backups = false;
1378  114 if (includeBackups)
1379    {
1380  74 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  74 backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVal, null,
1385  74 Platform.isHeadless() ? null : BackupFiles.ENABLED,
1386    !Platform.isHeadless());
1387    }
1388   
1389  114 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  114 boolean mkdirs = avm.getFromSubValArgOrPref(Arg.MKDIRS, subVal, null,
1399    "MKDIRS_OUTPUT", false);
1400   
1401  114 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  114 return true;
1413    }
1414   
 
1415  97 toggle public List<String> getErrors()
1416    {
1417  97 return errors;
1418    }
1419   
 
1420  125 toggle public String errorsToString()
1421    {
1422  125 StringBuilder sb = new StringBuilder();
1423  125 for (String error : errors)
1424    {
1425  0 if (sb.length() > 0)
1426  0 sb.append("\n");
1427  0 sb.append("- " + error);
1428    }
1429  125 return sb.toString();
1430    }
1431    }