Clover icon

Coverage Report

  1. Project Clover database Mon Nov 11 2024 20:42:03 GMT
  2. Package jalview.bin

File Commands.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% of files have more coverage

Code metrics

228
538
25
1
1,426
1,159
196
0.36
21.52
25
7.84

Classes

Class Line # Actions
Commands 86 538 196
0.6750948467.5%
 

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  130 toggle public Commands(ArgParser argparser, boolean headless)
105    {
106  130 this(Desktop.instance, argparser, headless);
107    }
108   
 
109  130 toggle public Commands(Desktop d, ArgParser argparser, boolean h)
110    {
111  130 argParser = argparser;
112  130 headless = h;
113  130 desktop = d;
114  130 afMap = new HashMap<>();
115    }
116   
 
117  130 toggle protected boolean processArgs()
118    {
119  130 if (argParser == null)
120    {
121  0 return true;
122    }
123   
124  130 boolean theseArgsWereParsed = false;
125   
126  130 if (argParser != null && argParser.getLinkedIds() != null)
127    {
128  130 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  643 while (af != null && af.getViewport().isCalcInProgress())
140    {
141  492 try
142    {
143  492 Thread.sleep(25);
144    } catch (Exception q)
145    {
146    }
147  492 ;
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  126 String errorsRaised = errorsToString();
173  126 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  126 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  125 argsWereParsed = theseArgsWereParsed;
189  125 return argsWereParsed;
190    }
191   
 
192  13 toggle public boolean commandArgsProvided()
193    {
194  13 return commandArgsProvided;
195    }
196   
 
197  137 toggle public boolean argsWereParsed()
198    {
199  137 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  117 toggle @Override
253    public void run()
254    {
255  117 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  282 while (structureViewer.isBusy() && tries > 0)
649    {
650  249 Thread.sleep(25);
651  249 if (structureViewer.isBusy())
652    {
653  216 tries--;
654  216 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 af.setAnnotationsVisibility(showSSAnnotations, true, false);
931  151 af.setAnnotationsVisibility(showAnnotations, false, true);
932   
933    // show temperature factor annotations?
934  151 if (hideTFrows)
935    {
936    // do this better (annotation types?)
937  0 List<String> hideThese = new ArrayList<>();
938  0 hideThese.add("Temperature Factor");
939  0 hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
940  0 AlignmentUtils.showOrHideSequenceAnnotations(
941    af.getCurrentView().getAlignment(), hideThese, null, false,
942    false);
943    }
944    }
945   
 
946  151 toggle protected void processGroovyScript(String id)
947    {
948  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
949  151 AlignFrame af = afMap.get(id);
950   
951  151 if (avm != null && !avm.containsArg(Arg.GROOVY))
952    {
953    // nothing to do
954  151 return;
955    }
956   
957  0 if (af == null)
958    {
959  0 addWarn("Groovy script does not have an alignment window. Proceeding with caution!");
960    }
961   
962  0 if (avm.containsArg(Arg.GROOVY))
963    {
964  0 for (ArgValue groovyAv : avm.getArgValueList(Arg.GROOVY))
965    {
966  0 String groovyscript = groovyAv.getValue();
967  0 if (groovyscript != null)
968    {
969    // Execute the groovy script after we've done all the rendering stuff
970    // and before any images or figures are generated.
971  0 Console.info("Executing script " + groovyscript);
972  0 Jalview.getInstance().executeGroovyScript(groovyscript, af);
973    }
974    }
975    }
976    }
977   
 
978  151 toggle protected boolean processImages(String id)
979    {
980  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
981  151 AlignFrame af = afMap.get(id);
982   
983  151 if (avm != null && !avm.containsArg(Arg.IMAGE))
984    {
985    // nothing to do
986  121 return true;
987    }
988   
989  30 if (af == null)
990    {
991  0 addWarn("Do not have an alignment window to create image from (id="
992    + id + "). Not proceeding.");
993  0 return false;
994    }
995   
996  30 Boolean isError = Boolean.valueOf(false);
997  30 if (avm.containsArg(Arg.IMAGE))
998    {
999  30 for (ArgValue imageAv : avm.getArgValueList(Arg.IMAGE))
1000    {
1001  30 String val = imageAv.getValue();
1002  30 SubVals imageSubVals = imageAv.getSubVals();
1003  30 String fileName = imageSubVals.getContent();
1004  30 File file = new File(fileName);
1005  30 String name = af.getName();
1006  30 String renderer = avm.getValueFromSubValOrArg(imageAv,
1007    Arg.TEXTRENDERER, imageSubVals);
1008  30 if (renderer == null)
1009  30 renderer = "text";
1010  30 String type = "png"; // default
1011   
1012  30 String scale = avm.getValueFromSubValOrArg(imageAv, Arg.SCALE,
1013    imageSubVals);
1014  30 String width = avm.getValueFromSubValOrArg(imageAv, Arg.WIDTH,
1015    imageSubVals);
1016  30 String height = avm.getValueFromSubValOrArg(imageAv, Arg.HEIGHT,
1017    imageSubVals);
1018  30 BitmapImageSizing userBis = ImageMaker
1019    .parseScaleWidthHeightStrings(scale, width, height);
1020   
1021  30 type = avm.getValueFromSubValOrArg(imageAv, Arg.TYPE, imageSubVals);
1022  30 if (type == null && fileName != null)
1023    {
1024  30 for (String ext : new String[] { "svg", "png", "html", "eps" })
1025    {
1026  120 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1027    {
1028  30 type = ext;
1029    }
1030    }
1031    }
1032    // for moment we disable JSON export
1033  30 Cache.setPropsAreReadOnly(true);
1034  30 Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
1035   
1036  30 String imageColour = avm.getValueFromSubValOrArg(imageAv,
1037    Arg.IMAGECOLOUR, imageSubVals);
1038  30 ColourSchemeI originalColourScheme = this.getColourScheme(af);
1039  30 this.colourAlignFrame(af, imageColour);
1040   
1041  30 Console.info("Writing " + file);
1042   
1043  30 boolean success = checksBeforeWritingToFile(avm, imageSubVals,
1044    false, fileName, "image", isError);
1045  30 if (!success)
1046    {
1047  0 continue;
1048    }
1049   
1050  30 try
1051    {
1052  30 switch (type)
1053    {
1054   
1055  1 case "svg":
1056  1 Console.debug("Outputting type '" + type + "' to " + fileName);
1057  1 af.createSVG(file, renderer);
1058  1 break;
1059   
1060  23 case "png":
1061  23 Console.debug("Outputting type '" + type + "' to " + fileName);
1062  23 af.createPNG(file, null, userBis);
1063  23 break;
1064   
1065  1 case "html":
1066  1 Console.debug("Outputting type '" + type + "' to " + fileName);
1067  1 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1068  1 htmlSVG.exportHTML(fileName, renderer);
1069  1 break;
1070   
1071  0 case "biojs":
1072  0 Console.debug(
1073    "Outputting BioJS MSA Viwer HTML file: " + fileName);
1074  0 try
1075    {
1076  0 BioJsHTMLOutput.refreshVersionInfo(
1077    BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1078    } catch (URISyntaxException e)
1079    {
1080  0 e.printStackTrace();
1081    }
1082  0 BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1083  0 bjs.exportHTML(fileName);
1084  0 break;
1085   
1086  5 case "eps":
1087  5 Console.debug("Outputting EPS file: " + fileName);
1088  5 af.createEPS(file, renderer);
1089  5 break;
1090   
1091  0 case "imagemap":
1092  0 Console.debug("Outputting ImageMap file: " + fileName);
1093  0 af.createImageMap(file, name);
1094  0 break;
1095   
1096  0 default:
1097  0 addWarn(Arg.IMAGE.argString() + " type '" + type
1098    + "' not known. Ignoring");
1099  0 break;
1100    }
1101    } catch (Exception ioex)
1102    {
1103  0 addError("Unexpected error during export to '" + fileName + "'",
1104    ioex);
1105  0 isError = true;
1106    }
1107   
1108  30 this.colourAlignFrame(af, originalColourScheme);
1109    }
1110    }
1111  30 return !isError;
1112    }
1113   
 
1114  151 toggle protected boolean processOutput(String id)
1115    {
1116  151 ArgValuesMap avm = argParser.getLinkedArgs(id);
1117  151 AlignFrame af = afMap.get(id);
1118   
1119  151 if (avm != null && !avm.containsArg(Arg.OUTPUT))
1120    {
1121    // nothing to do
1122  77 return true;
1123    }
1124   
1125  74 if (af == null)
1126    {
1127  0 addWarn("Do not have an alignment window (id=" + id
1128    + "). Not proceeding.");
1129  0 return false;
1130    }
1131   
1132  74 Boolean isError = Boolean.valueOf(false);
1133   
1134  74 if (avm.containsArg(Arg.OUTPUT))
1135    {
1136  74 for (ArgValue av : avm.getArgValueList(Arg.OUTPUT))
1137    {
1138  74 String val = av.getValue();
1139  74 SubVals subVals = av.getSubVals();
1140  74 String fileName = subVals.getContent();
1141  74 boolean stdout = ArgParser.STDOUTFILENAME.equals(fileName);
1142  74 File file = new File(fileName);
1143   
1144  74 String name = af.getName();
1145  74 String format = avm.getValueFromSubValOrArg(av, Arg.FORMAT,
1146    subVals);
1147  74 FileFormats ffs = FileFormats.getInstance();
1148  74 List<String> validFormats = ffs.getWritableFormats(false);
1149   
1150  74 FileFormatI ff = null;
1151  74 if (format == null && fileName != null)
1152    {
1153  71 FORMAT: for (String fname : validFormats)
1154    {
1155  365 FileFormatI tff = ffs.forName(fname);
1156  365 String[] extensions = tff.getExtensions().split(",");
1157  365 for (String ext : extensions)
1158    {
1159  655 if (fileName.toLowerCase(Locale.ROOT).endsWith("." + ext))
1160    {
1161  62 ff = tff;
1162  62 format = ff.getName();
1163  62 break FORMAT;
1164    }
1165    }
1166    }
1167    }
1168  74 if (ff == null && format != null)
1169    {
1170  3 ff = ffs.forName(format);
1171    }
1172  74 if (ff == null)
1173    {
1174  9 if (stdout)
1175    {
1176  9 ff = FileFormat.Fasta;
1177    }
1178    else
1179    {
1180  0 StringBuilder validSB = new StringBuilder();
1181  0 for (String f : validFormats)
1182    {
1183  0 if (validSB.length() > 0)
1184  0 validSB.append(", ");
1185  0 validSB.append(f);
1186  0 FileFormatI tff = ffs.forName(f);
1187  0 validSB.append(" (");
1188  0 validSB.append(tff.getExtensions());
1189  0 validSB.append(")");
1190    }
1191   
1192  0 addError("No valid format specified for "
1193    + Arg.OUTPUT.argString() + ". Valid formats are "
1194    + validSB.toString() + ".");
1195  0 continue;
1196    }
1197    }
1198   
1199  74 boolean success = checksBeforeWritingToFile(avm, subVals, true,
1200    fileName, ff.getName(), isError);
1201  74 if (!success)
1202    {
1203  0 continue;
1204    }
1205   
1206  74 boolean backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVals,
1207  74 null, Platform.isHeadless() ? null : BackupFiles.ENABLED,
1208    !Platform.isHeadless());
1209   
1210  74 Console.info("Writing " + fileName);
1211   
1212  74 af.saveAlignment(fileName, ff, stdout, backups);
1213  74 if (af.isSaveAlignmentSuccessful())
1214    {
1215  74 Console.debug("Written alignment '" + name + "' in "
1216    + ff.getName() + " format to '" + file + "'");
1217    }
1218    else
1219    {
1220  0 addError("Error writing file '" + file + "' in " + ff.getName()
1221    + " format!");
1222  0 isError = true;
1223  0 continue;
1224    }
1225   
1226    }
1227    }
1228  74 return !isError;
1229    }
1230   
 
1231  42 toggle private SequenceI getSpecifiedSequence(AlignFrame af, ArgValuesMap avm,
1232    ArgValue av)
1233    {
1234  42 SubVals subVals = av.getSubVals();
1235  42 ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID, true);
1236  42 SequenceI seq = null;
1237  42 if (subVals == null && idAv == null)
1238  0 return null;
1239  42 if (af == null || af.getCurrentView() == null)
1240    {
1241  0 return null;
1242    }
1243  42 AlignmentI al = af.getCurrentView().getAlignment();
1244  42 if (al == null)
1245    {
1246  0 return null;
1247    }
1248  42 if (subVals != null)
1249    {
1250  42 if (subVals.has(Arg.SEQID.getName()))
1251    {
1252  8 seq = al.findName(subVals.get(Arg.SEQID.getName()));
1253    }
1254  34 else if (-1 < subVals.getIndex()
1255    && subVals.getIndex() < al.getSequences().size())
1256    {
1257  0 seq = al.getSequenceAt(subVals.getIndex());
1258    }
1259    }
1260  42 if (seq == null && idAv != null)
1261    {
1262  12 seq = al.findName(idAv.getValue());
1263    }
1264  42 return seq;
1265    }
1266   
 
1267  0 toggle public AlignFrame[] getAlignFrames()
1268    {
1269  0 AlignFrame[] afs = null;
1270  0 if (afMap != null)
1271    {
1272  0 afs = (AlignFrame[]) afMap.values().toArray();
1273    }
1274   
1275  0 return afs;
1276    }
1277   
 
1278  0 toggle public List<StructureViewer> getStructureViewers()
1279    {
1280  0 List<StructureViewer> svs = null;
1281  0 if (svMap != null)
1282    {
1283  0 for (List<StructureViewer> svList : svMap.values())
1284    {
1285  0 if (svs == null)
1286    {
1287  0 svs = new ArrayList<>();
1288    }
1289  0 svs.addAll(svList);
1290    }
1291    }
1292  0 return svs;
1293    }
1294   
 
1295  53 toggle private void colourAlignFrame(AlignFrame af, String colour)
1296    {
1297    // use string "none" to remove colour scheme
1298  53 if (colour != null && "" != colour)
1299    {
1300  13 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
1301    af.getViewport(), af.getViewport().getAlignment(), colour);
1302  13 if (cs == null && !StringUtils.equalsIgnoreCase(colour, "none"))
1303    {
1304  0 addWarn("Couldn't parse '" + colour + "' as a colourscheme.");
1305    }
1306    else
1307    {
1308  13 Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
1309  13 colourAlignFrame(af, cs);
1310    }
1311    }
1312    }
1313   
 
1314  53 toggle private void colourAlignFrame(AlignFrame af, ColourSchemeI cs)
1315    {
1316  53 try
1317    {
1318  53 SwingUtilities.invokeAndWait(new Runnable()
1319    {
 
1320  53 toggle @Override
1321    public void run()
1322    {
1323    // Note that cs == null removes colour scheme from af
1324  53 af.changeColour(cs);
1325    }
1326    });
1327    } catch (Exception x)
1328    {
1329  0 Console.trace("Interrupted whilst waiting for colorAlignFrame action",
1330    x);
1331   
1332    }
1333    }
1334   
 
1335  40 toggle private ColourSchemeI getColourScheme(AlignFrame af)
1336    {
1337  40 return af.getViewport().getGlobalColourScheme();
1338    }
1339   
 
1340  0 toggle private void addInfo(String errorMessage)
1341    {
1342  0 Console.info(errorMessage);
1343  0 errors.add(errorMessage);
1344    }
1345   
 
1346  0 toggle private void addWarn(String errorMessage)
1347    {
1348  0 Console.warn(errorMessage);
1349  0 errors.add(errorMessage);
1350    }
1351   
 
1352  0 toggle private void addError(String errorMessage)
1353    {
1354  0 addError(errorMessage, null);
1355    }
1356   
 
1357  0 toggle private void addError(String errorMessage, Exception e)
1358    {
1359  0 Console.error(errorMessage, e);
1360  0 errors.add(errorMessage);
1361    }
1362   
 
1363  114 toggle private boolean checksBeforeWritingToFile(ArgValuesMap avm,
1364    SubVals subVal, boolean includeBackups, String filename,
1365    String adjective, Boolean isError)
1366    {
1367  114 File file = new File(filename);
1368   
1369  114 boolean overwrite = avm.getFromSubValArgOrPref(Arg.OVERWRITE, subVal,
1370    null, "OVERWRITE_OUTPUT", false);
1371  114 boolean stdout = false;
1372  114 boolean backups = false;
1373  114 if (includeBackups)
1374    {
1375  74 stdout = ArgParser.STDOUTFILENAME.equals(filename);
1376    // backups. Use the Arg.BACKUPS or subval "backups" setting first,
1377    // otherwise if headless assume false, if not headless use the user
1378    // preference with default true.
1379  74 backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVal, null,
1380  74 Platform.isHeadless() ? null : BackupFiles.ENABLED,
1381    !Platform.isHeadless());
1382    }
1383   
1384  114 if (file.exists() && !(overwrite || backups || stdout))
1385    {
1386  0 addWarn("Won't overwrite file '" + filename + "' without "
1387    + Arg.OVERWRITE.argString()
1388  0 + (includeBackups ? " or " + Arg.BACKUPS.argString() : "")
1389    + " set");
1390  0 return false;
1391    }
1392   
1393  114 boolean mkdirs = avm.getFromSubValArgOrPref(Arg.MKDIRS, subVal, null,
1394    "MKDIRS_OUTPUT", false);
1395   
1396  114 if (!FileUtils.checkParentDir(file, mkdirs))
1397    {
1398  0 addError("Directory '"
1399    + FileUtils.getParentDir(file).getAbsolutePath()
1400    + "' does not exist for " + adjective + " file '" + filename
1401    + "'."
1402  0 + (mkdirs ? "" : " Try using " + Arg.MKDIRS.argString()));
1403  0 isError = true;
1404  0 return false;
1405    }
1406   
1407  114 return true;
1408    }
1409   
 
1410  98 toggle public List<String> getErrors()
1411    {
1412  98 return errors;
1413    }
1414   
 
1415  126 toggle public String errorsToString()
1416    {
1417  126 StringBuilder sb = new StringBuilder();
1418  126 for (String error : errors)
1419    {
1420  0 if (sb.length() > 0)
1421  0 sb.append("\n");
1422  0 sb.append("- " + error);
1423    }
1424  126 return sb.toString();
1425    }
1426    }