Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

../../img/srcFileCovDistChart8.png
20% of files have more coverage

Code metrics

1,060
2,320
107
3
6,578
4,763
756
0.33
21.68
35.67
7.07

Classes

Class Line # Actions
Jalview2XML 211 2,307 747
0.7461738674.6%
Jalview2XML.SeqFref 369 11 8
0.00%
Jalview2XML.JvAnnotRow 3222 2 1
1.0100%
 

Contributing tests

This file is covered by 24 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.project;
22   
23    import static jalview.math.RotatableMatrix.Axis.X;
24    import static jalview.math.RotatableMatrix.Axis.Y;
25    import static jalview.math.RotatableMatrix.Axis.Z;
26   
27    import java.awt.Color;
28    import java.awt.Font;
29    import java.awt.Rectangle;
30    import java.io.BufferedReader;
31    import java.io.ByteArrayInputStream;
32    import java.io.File;
33    import java.io.FileInputStream;
34    import java.io.FileOutputStream;
35    import java.io.IOException;
36    import java.io.InputStream;
37    import java.io.InputStreamReader;
38    import java.io.OutputStream;
39    import java.io.OutputStreamWriter;
40    import java.io.PrintWriter;
41    import java.lang.reflect.InvocationTargetException;
42    import java.math.BigInteger;
43    import java.net.MalformedURLException;
44    import java.net.URL;
45    import java.util.ArrayList;
46    import java.util.Arrays;
47    import java.util.Collections;
48    import java.util.Enumeration;
49    import java.util.GregorianCalendar;
50    import java.util.HashMap;
51    import java.util.HashSet;
52    import java.util.Hashtable;
53    import java.util.IdentityHashMap;
54    import java.util.Iterator;
55    import java.util.LinkedHashMap;
56    import java.util.List;
57    import java.util.Map;
58    import java.util.Map.Entry;
59    import java.util.Set;
60    import java.util.Vector;
61    import java.util.jar.JarEntry;
62    import java.util.jar.JarInputStream;
63    import java.util.jar.JarOutputStream;
64   
65    import javax.swing.JInternalFrame;
66    import javax.swing.SwingUtilities;
67    import javax.xml.bind.JAXBContext;
68    import javax.xml.bind.JAXBElement;
69    import javax.xml.bind.Marshaller;
70    import javax.xml.datatype.DatatypeConfigurationException;
71    import javax.xml.datatype.DatatypeFactory;
72    import javax.xml.datatype.XMLGregorianCalendar;
73    import javax.xml.stream.XMLInputFactory;
74    import javax.xml.stream.XMLStreamReader;
75   
76    import jalview.analysis.Conservation;
77    import jalview.analysis.PCA;
78    import jalview.analysis.scoremodels.ScoreModels;
79    import jalview.analysis.scoremodels.SimilarityParams;
80    import jalview.api.FeatureColourI;
81    import jalview.api.ViewStyleI;
82    import jalview.api.analysis.ScoreModelI;
83    import jalview.api.analysis.SimilarityParamsI;
84    import jalview.api.structures.JalviewStructureDisplayI;
85    import jalview.bin.Cache;
86    import jalview.datamodel.AlignedCodonFrame;
87    import jalview.datamodel.Alignment;
88    import jalview.datamodel.AlignmentAnnotation;
89    import jalview.datamodel.AlignmentI;
90    import jalview.datamodel.DBRefEntry;
91    import jalview.datamodel.GeneLocus;
92    import jalview.datamodel.GraphLine;
93    import jalview.datamodel.PDBEntry;
94    import jalview.datamodel.Point;
95    import jalview.datamodel.RnaViewerModel;
96    import jalview.datamodel.SequenceFeature;
97    import jalview.datamodel.SequenceGroup;
98    import jalview.datamodel.SequenceI;
99    import jalview.datamodel.StructureViewerModel;
100    import jalview.datamodel.StructureViewerModel.StructureData;
101    import jalview.datamodel.features.FeatureMatcher;
102    import jalview.datamodel.features.FeatureMatcherI;
103    import jalview.datamodel.features.FeatureMatcherSet;
104    import jalview.datamodel.features.FeatureMatcherSetI;
105    import jalview.ext.varna.RnaModel;
106    import jalview.gui.AlignFrame;
107    import jalview.gui.AlignViewport;
108    import jalview.gui.AlignmentPanel;
109    import jalview.gui.AppVarna;
110    import jalview.gui.Desktop;
111    import jalview.gui.JvOptionPane;
112    import jalview.gui.OOMWarning;
113    import jalview.gui.PCAPanel;
114    import jalview.gui.PaintRefresher;
115    import jalview.gui.SplitFrame;
116    import jalview.gui.StructureViewer;
117    import jalview.gui.StructureViewer.ViewerType;
118    import jalview.gui.StructureViewerBase;
119    import jalview.gui.TreePanel;
120    import jalview.io.BackupFiles;
121    import jalview.io.DataSourceType;
122    import jalview.io.FileFormat;
123    import jalview.io.NewickFile;
124    import jalview.math.Matrix;
125    import jalview.math.MatrixI;
126    import jalview.renderer.ResidueShaderI;
127    import jalview.schemes.AnnotationColourGradient;
128    import jalview.schemes.ColourSchemeI;
129    import jalview.schemes.ColourSchemeProperty;
130    import jalview.schemes.FeatureColour;
131    import jalview.schemes.ResidueProperties;
132    import jalview.schemes.UserColourScheme;
133    import jalview.structure.StructureSelectionManager;
134    import jalview.structures.models.AAStructureBindingModel;
135    import jalview.util.Format;
136    import jalview.util.MessageManager;
137    import jalview.util.Platform;
138    import jalview.util.StringUtils;
139    import jalview.util.jarInputStreamProvider;
140    import jalview.util.matcher.Condition;
141    import jalview.viewmodel.AlignmentViewport;
142    import jalview.viewmodel.PCAModel;
143    import jalview.viewmodel.ViewportRanges;
144    import jalview.viewmodel.seqfeatures.FeatureRendererModel;
145    import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
146    import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
147    import jalview.ws.jws2.Jws2Discoverer;
148    import jalview.ws.jws2.dm.AAConSettings;
149    import jalview.ws.jws2.jabaws2.Jws2Instance;
150    import jalview.ws.params.ArgumentI;
151    import jalview.ws.params.AutoCalcSetting;
152    import jalview.ws.params.WsParamSetI;
153    import jalview.xml.binding.jalview.AlcodonFrame;
154    import jalview.xml.binding.jalview.AlcodonFrame.AlcodMap;
155    import jalview.xml.binding.jalview.Annotation;
156    import jalview.xml.binding.jalview.Annotation.ThresholdLine;
157    import jalview.xml.binding.jalview.AnnotationColourScheme;
158    import jalview.xml.binding.jalview.AnnotationElement;
159    import jalview.xml.binding.jalview.DoubleMatrix;
160    import jalview.xml.binding.jalview.DoubleVector;
161    import jalview.xml.binding.jalview.Feature;
162    import jalview.xml.binding.jalview.Feature.OtherData;
163    import jalview.xml.binding.jalview.FeatureMatcherSet.CompoundMatcher;
164    import jalview.xml.binding.jalview.FilterBy;
165    import jalview.xml.binding.jalview.JalviewModel;
166    import jalview.xml.binding.jalview.JalviewModel.FeatureSettings;
167    import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Group;
168    import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Setting;
169    import jalview.xml.binding.jalview.JalviewModel.JGroup;
170    import jalview.xml.binding.jalview.JalviewModel.JSeq;
171    import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids;
172    import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids.StructureState;
173    import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer;
174    import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer.SecondaryStructure;
175    import jalview.xml.binding.jalview.JalviewModel.PcaViewer;
176    import jalview.xml.binding.jalview.JalviewModel.PcaViewer.Axis;
177    import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMax;
178    import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMin;
179    import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SequencePoint;
180    import jalview.xml.binding.jalview.JalviewModel.Tree;
181    import jalview.xml.binding.jalview.JalviewModel.UserColours;
182    import jalview.xml.binding.jalview.JalviewModel.Viewport;
183    import jalview.xml.binding.jalview.JalviewModel.Viewport.CalcIdParam;
184    import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns;
185    import jalview.xml.binding.jalview.JalviewUserColours;
186    import jalview.xml.binding.jalview.JalviewUserColours.Colour;
187    import jalview.xml.binding.jalview.MapListType.MapListFrom;
188    import jalview.xml.binding.jalview.MapListType.MapListTo;
189    import jalview.xml.binding.jalview.Mapping;
190    import jalview.xml.binding.jalview.NoValueColour;
191    import jalview.xml.binding.jalview.ObjectFactory;
192    import jalview.xml.binding.jalview.PcaDataType;
193    import jalview.xml.binding.jalview.Pdbentry.Property;
194    import jalview.xml.binding.jalview.Sequence;
195    import jalview.xml.binding.jalview.Sequence.DBRef;
196    import jalview.xml.binding.jalview.SequenceSet;
197    import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties;
198    import jalview.xml.binding.jalview.ThresholdType;
199    import jalview.xml.binding.jalview.VAMSAS;
200   
201    /**
202    * Write out the current jalview desktop state as a Jalview XML stream.
203    *
204    * Note: the vamsas objects referred to here are primitive versions of the
205    * VAMSAS project schema elements - they are not the same and most likely never
206    * will be :)
207    *
208    * @author $author$
209    * @version $Revision: 1.134 $
210    */
 
211    public class Jalview2XML
212    {
213   
214    // BH 2018 we add the .jvp binary extension to J2S so that
215    // it will declare that binary when we do the file save from the browser
216   
 
217  1 toggle static
218    {
219  1 Platform.addJ2SBinaryType(".jvp?");
220    }
221   
222    private static final String VIEWER_PREFIX = "viewer_";
223   
224    private static final String RNA_PREFIX = "rna_";
225   
226    private static final String UTF_8 = "UTF-8";
227   
228    /**
229    * prefix for recovering datasets for alignments with multiple views where
230    * non-existent dataset IDs were written for some views
231    */
232    private static final String UNIQSEQSETID = "uniqueSeqSetId.";
233   
234    // use this with nextCounter() to make unique names for entities
235    private int counter = 0;
236   
237    /*
238    * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
239    * of sequence objects are created.
240    */
241    IdentityHashMap<SequenceI, String> seqsToIds = null;
242   
243    /**
244    * jalview XML Sequence ID to jalview sequence object reference (both dataset
245    * and alignment sequences. Populated as XML reps of sequence objects are
246    * created.)
247    */
248    Map<String, SequenceI> seqRefIds = null;
249   
250    Map<String, SequenceI> incompleteSeqs = null;
251   
252    List<SeqFref> frefedSequence = null;
253   
254    boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
255   
256    /*
257    * Map of reconstructed AlignFrame objects that appear to have come from
258    * SplitFrame objects (have a dna/protein complement view).
259    */
260    private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<>();
261   
262    /*
263    * Map from displayed rna structure models to their saved session state jar
264    * entry names
265    */
266    private Map<RnaModel, String> rnaSessions = new HashMap<>();
267   
268    /**
269    * A helper method for safely using the value of an optional attribute that
270    * may be null if not present in the XML. Answers the boolean value, or false
271    * if null.
272    *
273    * @param b
274    * @return
275    */
 
276  3470 toggle public static boolean safeBoolean(Boolean b)
277    {
278  3470 return b == null ? false : b.booleanValue();
279    }
280   
281    /**
282    * A helper method for safely using the value of an optional attribute that
283    * may be null if not present in the XML. Answers the integer value, or zero
284    * if null.
285    *
286    * @param i
287    * @return
288    */
 
289  269431 toggle public static int safeInt(Integer i)
290    {
291  269431 return i == null ? 0 : i.intValue();
292    }
293   
294    /**
295    * A helper method for safely using the value of an optional attribute that
296    * may be null if not present in the XML. Answers the float value, or zero if
297    * null.
298    *
299    * @param f
300    * @return
301    */
 
302  401690 toggle public static float safeFloat(Float f)
303    {
304  401690 return f == null ? 0f : f.floatValue();
305    }
306   
307    /**
308    * create/return unique hash string for sq
309    *
310    * @param sq
311    * @return new or existing unique string for sq
312    */
 
313  1023 toggle String seqHash(SequenceI sq)
314    {
315  1023 if (seqsToIds == null)
316    {
317  0 initSeqRefs();
318    }
319  1023 if (seqsToIds.containsKey(sq))
320    {
321  515 return seqsToIds.get(sq);
322    }
323    else
324    {
325    // create sequential key
326  508 String key = "sq" + (seqsToIds.size() + 1);
327  508 key = makeHashCode(sq, key); // check we don't have an external reference
328    // for it already.
329  508 seqsToIds.put(sq, key);
330  508 return key;
331    }
332    }
333   
 
334  72 toggle void initSeqRefs()
335    {
336  72 if (seqsToIds == null)
337    {
338  39 seqsToIds = new IdentityHashMap<>();
339    }
340  72 if (seqRefIds == null)
341    {
342  39 seqRefIds = new HashMap<>();
343    }
344  72 if (incompleteSeqs == null)
345    {
346  39 incompleteSeqs = new HashMap<>();
347    }
348  72 if (frefedSequence == null)
349    {
350  39 frefedSequence = new ArrayList<>();
351    }
352    }
353   
 
354  11 toggle public Jalview2XML()
355    {
356    }
357   
 
358  28 toggle public Jalview2XML(boolean raiseGUI)
359    {
360  28 this.raiseGUI = raiseGUI;
361    }
362   
363    /**
364    * base class for resolving forward references to sequences by their ID
365    *
366    * @author jprocter
367    *
368    */
 
369    abstract class SeqFref
370    {
371    String sref;
372   
373    String type;
374   
 
375  0 toggle public SeqFref(String _sref, String type)
376    {
377  0 sref = _sref;
378  0 this.type = type;
379    }
380   
 
381  0 toggle public String getSref()
382    {
383  0 return sref;
384    }
385   
 
386  0 toggle public SequenceI getSrefSeq()
387    {
388  0 return seqRefIds.get(sref);
389    }
390   
 
391  0 toggle public boolean isResolvable()
392    {
393  0 return seqRefIds.get(sref) != null;
394    }
395   
 
396  0 toggle public SequenceI getSrefDatasetSeq()
397    {
398  0 SequenceI sq = seqRefIds.get(sref);
399  0 if (sq != null)
400    {
401  0 while (sq.getDatasetSequence() != null)
402    {
403  0 sq = sq.getDatasetSequence();
404    }
405    }
406  0 return sq;
407    }
408   
409    /**
410    * @return true if the forward reference was fully resolved
411    */
412    abstract boolean resolve();
413   
 
414  0 toggle @Override
415    public String toString()
416    {
417  0 return type + " reference to " + sref;
418    }
419    }
420   
421    /**
422    * create forward reference for a mapping
423    *
424    * @param sref
425    * @param _jmap
426    * @return
427    */
 
428  0 toggle public SeqFref newMappingRef(final String sref,
429    final jalview.datamodel.Mapping _jmap)
430    {
431  0 SeqFref fref = new SeqFref(sref, "Mapping")
432    {
433    public jalview.datamodel.Mapping jmap = _jmap;
434   
 
435  0 toggle @Override
436    boolean resolve()
437    {
438  0 SequenceI seq = getSrefDatasetSeq();
439  0 if (seq == null)
440    {
441  0 return false;
442    }
443  0 jmap.setTo(seq);
444  0 return true;
445    }
446    };
447  0 return fref;
448    }
449   
 
450  0 toggle public SeqFref newAlcodMapRef(final String sref,
451    final AlignedCodonFrame _cf,
452    final jalview.datamodel.Mapping _jmap)
453    {
454   
455  0 SeqFref fref = new SeqFref(sref, "Codon Frame")
456    {
457    AlignedCodonFrame cf = _cf;
458   
459    public jalview.datamodel.Mapping mp = _jmap;
460   
 
461  0 toggle @Override
462    public boolean isResolvable()
463    {
464  0 return super.isResolvable() && mp.getTo() != null;
465    }
466   
 
467  0 toggle @Override
468    boolean resolve()
469    {
470  0 SequenceI seq = getSrefDatasetSeq();
471  0 if (seq == null)
472    {
473  0 return false;
474    }
475  0 cf.addMap(seq, mp.getTo(), mp.getMap());
476  0 return true;
477    }
478    };
479  0 return fref;
480    }
481   
 
482  21 toggle public void resolveFrefedSequences()
483    {
484  21 Iterator<SeqFref> nextFref = frefedSequence.iterator();
485  21 int toresolve = frefedSequence.size();
486  21 int unresolved = 0, failedtoresolve = 0;
487  21 while (nextFref.hasNext())
488    {
489  0 SeqFref ref = nextFref.next();
490  0 if (ref.isResolvable())
491    {
492  0 try
493    {
494  0 if (ref.resolve())
495    {
496  0 nextFref.remove();
497    }
498    else
499    {
500  0 failedtoresolve++;
501    }
502    } catch (Exception x)
503    {
504  0 System.err.println(
505    "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
506    + ref.getSref());
507  0 x.printStackTrace();
508  0 failedtoresolve++;
509    }
510    }
511    else
512    {
513  0 unresolved++;
514    }
515    }
516  21 if (unresolved > 0)
517    {
518  0 System.err.println("Jalview Project Import: There were " + unresolved
519    + " forward references left unresolved on the stack.");
520    }
521  21 if (failedtoresolve > 0)
522    {
523  0 System.err.println("SERIOUS! " + failedtoresolve
524    + " resolvable forward references failed to resolve.");
525    }
526  21 if (incompleteSeqs != null && incompleteSeqs.size() > 0)
527    {
528  0 System.err.println(
529    "Jalview Project Import: There are " + incompleteSeqs.size()
530    + " sequences which may have incomplete metadata.");
531  0 if (incompleteSeqs.size() < 10)
532    {
533  0 for (SequenceI s : incompleteSeqs.values())
534    {
535  0 System.err.println(s.toString());
536    }
537    }
538    else
539    {
540  0 System.err.println(
541    "Too many to report. Skipping output of incomplete sequences.");
542    }
543    }
544    }
545   
546    /**
547    * This maintains a map of viewports, the key being the seqSetId. Important to
548    * set historyItem and redoList for multiple views
549    */
550    Map<String, AlignViewport> viewportsAdded = new HashMap<>();
551   
552    Map<String, AlignmentAnnotation> annotationIds = new HashMap<>();
553   
554    String uniqueSetSuffix = "";
555   
556    /**
557    * List of pdbfiles added to Jar
558    */
559    List<String> pdbfiles = null;
560   
561    // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
 
562  7 toggle public void saveState(File statefile)
563    {
564  7 FileOutputStream fos = null;
565   
566  7 try
567    {
568   
569  7 fos = new FileOutputStream(statefile);
570   
571  7 JarOutputStream jout = new JarOutputStream(fos);
572  7 saveState(jout);
573  7 fos.close();
574   
575    } catch (Exception e)
576    {
577  0 Cache.log.error("Couln't write Jalview state to " + statefile, e);
578    // TODO: inform user of the problem - they need to know if their data was
579    // not saved !
580  0 if (errorMessage == null)
581    {
582  0 errorMessage = "Did't write Jalview Archive to output file '"
583    + statefile + "' - See console error log for details";
584    }
585    else
586    {
587  0 errorMessage += "(Didn't write Jalview Archive to output file '"
588    + statefile + ")";
589    }
590  0 e.printStackTrace();
591    } finally
592    {
593  7 if (fos != null)
594    {
595  7 try
596    {
597  7 fos.close();
598    } catch (IOException e)
599    {
600    // ignore
601    }
602    }
603    }
604  7 reportErrors();
605    }
606   
607    /**
608    * Writes a jalview project archive to the given Jar output stream.
609    *
610    * @param jout
611    */
 
612  7 toggle public void saveState(JarOutputStream jout)
613    {
614  7 AlignFrame[] frames = Desktop.getAlignFrames();
615   
616  7 if (frames == null)
617    {
618  0 return;
619    }
620  7 saveAllFrames(Arrays.asList(frames), jout);
621    }
622   
623    /**
624    * core method for storing state for a set of AlignFrames.
625    *
626    * @param frames
627    * - frames involving all data to be exported (including containing
628    * splitframes)
629    * @param jout
630    * - project output stream
631    */
 
632  12 toggle private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
633    {
634  12 Hashtable<String, AlignFrame> dsses = new Hashtable<>();
635   
636    /*
637    * ensure cached data is clear before starting
638    */
639    // todo tidy up seqRefIds, seqsToIds initialisation / reset
640  12 rnaSessions.clear();
641  12 splitFrameCandidates.clear();
642   
643  12 try
644    {
645   
646    // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
647    // //////////////////////////////////////////////////
648   
649  12 List<String> shortNames = new ArrayList<>();
650  12 List<String> viewIds = new ArrayList<>();
651   
652    // REVERSE ORDER
653  28 for (int i = frames.size() - 1; i > -1; i--)
654    {
655  16 AlignFrame af = frames.get(i);
656    // skip ?
657  16 if (skipList != null && skipList
658    .containsKey(af.getViewport().getSequenceSetId()))
659    {
660  0 continue;
661    }
662   
663  16 String shortName = makeFilename(af, shortNames);
664   
665  16 int apSize = af.getAlignPanels().size();
666   
667  39 for (int ap = 0; ap < apSize; ap++)
668    {
669  23 AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
670    .get(ap);
671  23 String fileName = apSize == 1 ? shortName : ap + shortName;
672  23 if (!fileName.endsWith(".xml"))
673    {
674  0 fileName = fileName + ".xml";
675    }
676   
677  23 saveState(apanel, fileName, jout, viewIds);
678   
679  23 String dssid = getDatasetIdRef(
680    af.getViewport().getAlignment().getDataset());
681  23 if (!dsses.containsKey(dssid))
682    {
683  16 dsses.put(dssid, af);
684    }
685    }
686    }
687   
688  12 writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
689    jout);
690   
691  12 try
692    {
693  12 jout.flush();
694    } catch (Exception foo)
695    {
696    }
697  12 jout.close();
698    } catch (Exception ex)
699    {
700    // TODO: inform user of the problem - they need to know if their data was
701    // not saved !
702  0 if (errorMessage == null)
703    {
704  0 errorMessage = "Couldn't write Jalview Archive - see error output for details";
705    }
706  0 ex.printStackTrace();
707    }
708    }
709   
710    /**
711    * Generates a distinct file name, based on the title of the AlignFrame, by
712    * appending _n for increasing n until an unused name is generated. The new
713    * name (without its extension) is added to the list.
714    *
715    * @param af
716    * @param namesUsed
717    * @return the generated name, with .xml extension
718    */
 
719  16 toggle protected String makeFilename(AlignFrame af, List<String> namesUsed)
720    {
721  16 String shortName = af.getTitle();
722   
723  16 if (shortName.indexOf(File.separatorChar) > -1)
724    {
725  8 shortName = shortName
726    .substring(shortName.lastIndexOf(File.separatorChar) + 1);
727    }
728   
729  16 int count = 1;
730   
731  26 while (namesUsed.contains(shortName))
732    {
733  10 if (shortName.endsWith("_" + (count - 1)))
734    {
735  6 shortName = shortName.substring(0, shortName.lastIndexOf("_"));
736    }
737   
738  10 shortName = shortName.concat("_" + count);
739  10 count++;
740    }
741   
742  16 namesUsed.add(shortName);
743   
744  16 if (!shortName.endsWith(".xml"))
745    {
746  16 shortName = shortName + ".xml";
747    }
748  16 return shortName;
749    }
750   
751    // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
 
752  5 toggle public boolean saveAlignment(AlignFrame af, String jarFile,
753    String fileName)
754    {
755  5 try
756    {
757    // create backupfiles object and get new temp filename destination
758  5 boolean doBackup = BackupFiles.getEnabled();
759  5 BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
760  5 FileOutputStream fos = new FileOutputStream(doBackup ?
761    backupfiles.getTempFilePath() : jarFile);
762   
763  5 JarOutputStream jout = new JarOutputStream(fos);
764  5 List<AlignFrame> frames = new ArrayList<>();
765   
766    // resolve splitframes
767  5 if (af.getViewport().getCodingComplement() != null)
768    {
769  0 frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
770    }
771    else
772    {
773  5 frames.add(af);
774    }
775  5 saveAllFrames(frames, jout);
776  5 try
777    {
778  5 jout.flush();
779    } catch (Exception foo)
780    {
781    }
782  5 jout.close();
783  5 boolean success = true;
784   
785  5 if (doBackup)
786    {
787  5 backupfiles.setWriteSuccess(success);
788  5 success = backupfiles.rollBackupsAndRenameTempFile();
789    }
790   
791  5 return success;
792    } catch (Exception ex)
793    {
794  0 errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
795  0 ex.printStackTrace();
796  0 return false;
797    }
798    }
799   
 
800  12 toggle private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
801    String fileName, JarOutputStream jout)
802    {
803   
804  12 for (String dssids : dsses.keySet())
805    {
806  16 AlignFrame _af = dsses.get(dssids);
807  16 String jfileName = fileName + " Dataset for " + _af.getTitle();
808  16 if (!jfileName.endsWith(".xml"))
809    {
810  16 jfileName = jfileName + ".xml";
811    }
812  16 saveState(_af.alignPanel, jfileName, true, jout, null);
813    }
814    }
815   
816    /**
817    * create a JalviewModel from an alignment view and marshall it to a
818    * JarOutputStream
819    *
820    * @param ap
821    * panel to create jalview model for
822    * @param fileName
823    * name of alignment panel written to output stream
824    * @param jout
825    * jar output stream
826    * @param viewIds
827    * @param out
828    * jar entry name
829    */
 
830  29 toggle public JalviewModel saveState(AlignmentPanel ap, String fileName,
831    JarOutputStream jout, List<String> viewIds)
832    {
833  29 return saveState(ap, fileName, false, jout, viewIds);
834    }
835   
836    /**
837    * create a JalviewModel from an alignment view and marshall it to a
838    * JarOutputStream
839    *
840    * @param ap
841    * panel to create jalview model for
842    * @param fileName
843    * name of alignment panel written to output stream
844    * @param storeDS
845    * when true, only write the dataset for the alignment, not the data
846    * associated with the view.
847    * @param jout
848    * jar output stream
849    * @param out
850    * jar entry name
851    */
 
852  45 toggle public JalviewModel saveState(AlignmentPanel ap, String fileName,
853    boolean storeDS, JarOutputStream jout, List<String> viewIds)
854    {
855  45 if (viewIds == null)
856    {
857  22 viewIds = new ArrayList<>();
858    }
859   
860  45 initSeqRefs();
861   
862  45 List<UserColourScheme> userColours = new ArrayList<>();
863   
864  45 AlignViewport av = ap.av;
865  45 ViewportRanges vpRanges = av.getRanges();
866   
867  45 final ObjectFactory objectFactory = new ObjectFactory();
868  45 JalviewModel object = objectFactory.createJalviewModel();
869  45 object.setVamsasModel(new VAMSAS());
870   
871    // object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
872  45 try
873    {
874  45 GregorianCalendar c = new GregorianCalendar();
875  45 DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
876  45 XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar);
877  45 object.setCreationDate(now);
878    } catch (DatatypeConfigurationException e)
879    {
880  0 System.err.println("error writing date: " + e.toString());
881    }
882  45 object.setVersion(
883    jalview.bin.Cache.getDefault("VERSION", "Development Build"));
884   
885    /**
886    * rjal is full height alignment, jal is actual alignment with full metadata
887    * but excludes hidden sequences.
888    */
889  45 jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
890   
891  45 if (av.hasHiddenRows())
892    {
893  19 rjal = jal.getHiddenSequences().getFullAlignment();
894    }
895   
896  45 SequenceSet vamsasSet = new SequenceSet();
897  45 Sequence vamsasSeq;
898    // JalviewModelSequence jms = new JalviewModelSequence();
899   
900  45 vamsasSet.setGapChar(jal.getGapCharacter() + "");
901   
902  45 if (jal.getDataset() != null)
903    {
904    // dataset id is the dataset's hashcode
905  45 vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
906  45 if (storeDS)
907    {
908    // switch jal and the dataset
909  16 jal = jal.getDataset();
910  16 rjal = jal;
911    }
912    }
913  45 if (jal.getProperties() != null)
914    {
915  1 Enumeration en = jal.getProperties().keys();
916  22 while (en.hasMoreElements())
917    {
918  21 String key = en.nextElement().toString();
919  21 SequenceSetProperties ssp = new SequenceSetProperties();
920  21 ssp.setKey(key);
921  21 ssp.setValue(jal.getProperties().get(key).toString());
922    // vamsasSet.addSequenceSetProperties(ssp);
923  21 vamsasSet.getSequenceSetProperties().add(ssp);
924    }
925    }
926   
927  45 JSeq jseq;
928  45 Set<String> calcIdSet = new HashSet<>();
929    // record the set of vamsas sequence XML POJO we create.
930  45 HashMap<String, Sequence> vamsasSetIds = new HashMap<>();
931    // SAVE SEQUENCES
932  45 for (final SequenceI jds : rjal.getSequences())
933    {
934  669 final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
935    : jds.getDatasetSequence();
936  669 String id = seqHash(jds);
937  669 if (vamsasSetIds.get(id) == null)
938    {
939  669 if (seqRefIds.get(id) != null && !storeDS)
940    {
941    // This happens for two reasons: 1. multiple views are being
942    // serialised.
943    // 2. the hashCode has collided with another sequence's code. This
944    // DOES
945    // HAPPEN! (PF00072.15.stk does this)
946    // JBPNote: Uncomment to debug writing out of files that do not read
947    // back in due to ArrayOutOfBoundExceptions.
948    // System.err.println("vamsasSeq backref: "+id+"");
949    // System.err.println(jds.getName()+"
950    // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
951    // System.err.println("Hashcode: "+seqHash(jds));
952    // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
953    // System.err.println(rsq.getName()+"
954    // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
955    // System.err.println("Hashcode: "+seqHash(rsq));
956    }
957    else
958    {
959  496 vamsasSeq = createVamsasSequence(id, jds);
960    // vamsasSet.addSequence(vamsasSeq);
961  496 vamsasSet.getSequence().add(vamsasSeq);
962  496 vamsasSetIds.put(id, vamsasSeq);
963  496 seqRefIds.put(id, jds);
964    }
965    }
966  669 jseq = new JSeq();
967  669 jseq.setStart(jds.getStart());
968  669 jseq.setEnd(jds.getEnd());
969  669 jseq.setColour(av.getSequenceColour(jds).getRGB());
970   
971  669 jseq.setId(id); // jseq id should be a string not a number
972  669 if (!storeDS)
973    {
974    // Store any sequences this sequence represents
975  427 if (av.hasHiddenRows())
976    {
977    // use rjal, contains the full height alignment
978  190 jseq.setHidden(
979    av.getAlignment().getHiddenSequences().isHidden(jds));
980   
981  190 if (av.isHiddenRepSequence(jds))
982    {
983  2 jalview.datamodel.SequenceI[] reps = av
984    .getRepresentedSequences(jds).getSequencesInOrder(rjal);
985   
986  6 for (int h = 0; h < reps.length; h++)
987    {
988  4 if (reps[h] != jds)
989    {
990    // jseq.addHiddenSequences(rjal.findIndex(reps[h]));
991  2 jseq.getHiddenSequences().add(rjal.findIndex(reps[h]));
992    }
993    }
994    }
995    }
996    // mark sequence as reference - if it is the reference for this view
997  427 if (jal.hasSeqrep())
998    {
999  80 jseq.setViewreference(jds == jal.getSeqrep());
1000    }
1001    }
1002   
1003    // TODO: omit sequence features from each alignment view's XML dump if we
1004    // are storing dataset
1005  669 List<SequenceFeature> sfs = jds.getSequenceFeatures();
1006  669 for (SequenceFeature sf : sfs)
1007    {
1008    // Features features = new Features();
1009  10860 Feature features = new Feature();
1010   
1011  10860 features.setBegin(sf.getBegin());
1012  10860 features.setEnd(sf.getEnd());
1013  10860 features.setDescription(sf.getDescription());
1014  10860 features.setType(sf.getType());
1015  10860 features.setFeatureGroup(sf.getFeatureGroup());
1016  10860 features.setScore(sf.getScore());
1017  10860 if (sf.links != null)
1018    {
1019  5344 for (int l = 0; l < sf.links.size(); l++)
1020    {
1021  2672 OtherData keyValue = new OtherData();
1022  2672 keyValue.setKey("LINK_" + l);
1023  2672 keyValue.setValue(sf.links.elementAt(l).toString());
1024    // features.addOtherData(keyValue);
1025  2672 features.getOtherData().add(keyValue);
1026    }
1027    }
1028  10860 if (sf.otherDetails != null)
1029    {
1030    /*
1031    * save feature attributes, which may be simple strings or
1032    * map valued (have sub-attributes)
1033    */
1034  3108 for (Entry<String, Object> entry : sf.otherDetails.entrySet())
1035    {
1036  3128 String key = entry.getKey();
1037  3128 Object value = entry.getValue();
1038  3128 if (value instanceof Map<?, ?>)
1039    {
1040  20 for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
1041    .entrySet())
1042    {
1043  20 OtherData otherData = new OtherData();
1044  20 otherData.setKey(key);
1045  20 otherData.setKey2(subAttribute.getKey());
1046  20 otherData.setValue(subAttribute.getValue().toString());
1047    // features.addOtherData(otherData);
1048  20 features.getOtherData().add(otherData);
1049    }
1050    }
1051    else
1052    {
1053  3108 OtherData otherData = new OtherData();
1054  3108 otherData.setKey(key);
1055  3108 otherData.setValue(value.toString());
1056    // features.addOtherData(otherData);
1057  3108 features.getOtherData().add(otherData);
1058    }
1059    }
1060    }
1061   
1062    // jseq.addFeatures(features);
1063  10860 jseq.getFeatures().add(features);
1064    }
1065   
1066  669 if (jdatasq.getAllPDBEntries() != null)
1067    {
1068  669 Enumeration<PDBEntry> en = jdatasq.getAllPDBEntries().elements();
1069  731 while (en.hasMoreElements())
1070    {
1071  62 Pdbids pdb = new Pdbids();
1072  62 jalview.datamodel.PDBEntry entry = en.nextElement();
1073   
1074  62 String pdbId = entry.getId();
1075  62 pdb.setId(pdbId);
1076  62 pdb.setType(entry.getType());
1077   
1078    /*
1079    * Store any structure views associated with this sequence. This
1080    * section copes with duplicate entries in the project, so a dataset
1081    * only view *should* be coped with sensibly.
1082    */
1083    // This must have been loaded, is it still visible?
1084  62 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1085  62 String matchedFile = null;
1086  352 for (int f = frames.length - 1; f > -1; f--)
1087    {
1088  290 if (frames[f] instanceof StructureViewerBase)
1089    {
1090  48 StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
1091  48 matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds,
1092    matchedFile, viewFrame);
1093    /*
1094    * Only store each structure viewer's state once in the project
1095    * jar. First time through only (storeDS==false)
1096    */
1097  48 String viewId = viewFrame.getViewId();
1098  48 String viewerType = viewFrame.getViewerType().toString();
1099  48 if (!storeDS && !viewIds.contains(viewId))
1100    {
1101  2 viewIds.add(viewId);
1102  2 File viewerState = viewFrame.saveSession();
1103  2 if (viewerState != null)
1104    {
1105  2 copyFileToJar(jout, viewerState.getPath(),
1106    getViewerJarEntryName(viewId), viewerType);
1107    }
1108    else
1109    {
1110  0 Cache.log.error("Failed to save viewer state for "
1111    +
1112    viewerType);
1113    }
1114    }
1115    }
1116    }
1117   
1118  62 if (matchedFile != null || entry.getFile() != null)
1119    {
1120  46 if (entry.getFile() != null)
1121    {
1122    // use entry's file
1123  46 matchedFile = entry.getFile();
1124    }
1125  46 pdb.setFile(matchedFile); // entry.getFile());
1126  46 if (pdbfiles == null)
1127    {
1128  4 pdbfiles = new ArrayList<>();
1129    }
1130   
1131  46 if (!pdbfiles.contains(pdbId))
1132    {
1133  4 pdbfiles.add(pdbId);
1134  4 copyFileToJar(jout, matchedFile, pdbId, pdbId);
1135    }
1136    }
1137   
1138  62 Enumeration<String> props = entry.getProperties();
1139  62 if (props.hasMoreElements())
1140    {
1141    // PdbentryItem item = new PdbentryItem();
1142  92 while (props.hasMoreElements())
1143    {
1144  46 Property prop = new Property();
1145  46 String key = props.nextElement();
1146  46 prop.setName(key);
1147  46 prop.setValue(entry.getProperty(key).toString());
1148    // item.addProperty(prop);
1149  46 pdb.getProperty().add(prop);
1150    }
1151    // pdb.addPdbentryItem(item);
1152    }
1153   
1154    // jseq.addPdbids(pdb);
1155  62 jseq.getPdbids().add(pdb);
1156    }
1157    }
1158   
1159  669 saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS);
1160   
1161    // jms.addJSeq(jseq);
1162  669 object.getJSeq().add(jseq);
1163    }
1164   
1165  45 if (!storeDS && av.hasHiddenRows())
1166    {
1167  12 jal = av.getAlignment();
1168    }
1169    // SAVE MAPPINGS
1170    // FOR DATASET
1171  45 if (storeDS && jal.getCodonFrames() != null)
1172    {
1173  16 List<AlignedCodonFrame> jac = jal.getCodonFrames();
1174  16 for (AlignedCodonFrame acf : jac)
1175    {
1176  0 AlcodonFrame alc = new AlcodonFrame();
1177  0 if (acf.getProtMappings() != null
1178    && acf.getProtMappings().length > 0)
1179    {
1180  0 boolean hasMap = false;
1181  0 SequenceI[] dnas = acf.getdnaSeqs();
1182  0 jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
1183  0 for (int m = 0; m < pmaps.length; m++)
1184    {
1185  0 AlcodMap alcmap = new AlcodMap();
1186  0 alcmap.setDnasq(seqHash(dnas[m]));
1187  0 alcmap.setMapping(
1188    createVamsasMapping(pmaps[m], dnas[m], null, false));
1189    // alc.addAlcodMap(alcmap);
1190  0 alc.getAlcodMap().add(alcmap);
1191  0 hasMap = true;
1192    }
1193  0 if (hasMap)
1194    {
1195    // vamsasSet.addAlcodonFrame(alc);
1196  0 vamsasSet.getAlcodonFrame().add(alc);
1197    }
1198    }
1199    // TODO: delete this ? dead code from 2.8.3->2.9 ?
1200    // {
1201    // AlcodonFrame alc = new AlcodonFrame();
1202    // vamsasSet.addAlcodonFrame(alc);
1203    // for (int p = 0; p < acf.aaWidth; p++)
1204    // {
1205    // Alcodon cmap = new Alcodon();
1206    // if (acf.codons[p] != null)
1207    // {
1208    // // Null codons indicate a gapped column in the translated peptide
1209    // // alignment.
1210    // cmap.setPos1(acf.codons[p][0]);
1211    // cmap.setPos2(acf.codons[p][1]);
1212    // cmap.setPos3(acf.codons[p][2]);
1213    // }
1214    // alc.addAlcodon(cmap);
1215    // }
1216    // if (acf.getProtMappings() != null
1217    // && acf.getProtMappings().length > 0)
1218    // {
1219    // SequenceI[] dnas = acf.getdnaSeqs();
1220    // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
1221    // for (int m = 0; m < pmaps.length; m++)
1222    // {
1223    // AlcodMap alcmap = new AlcodMap();
1224    // alcmap.setDnasq(seqHash(dnas[m]));
1225    // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
1226    // false));
1227    // alc.addAlcodMap(alcmap);
1228    // }
1229    // }
1230    }
1231    }
1232   
1233    // SAVE TREES
1234    // /////////////////////////////////
1235  45 if (!storeDS && av.getCurrentTree() != null)
1236    {
1237    // FIND ANY ASSOCIATED TREES
1238    // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
1239  2 if (Desktop.desktop != null)
1240    {
1241  2 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1242   
1243  12 for (int t = 0; t < frames.length; t++)
1244    {
1245  10 if (frames[t] instanceof TreePanel)
1246    {
1247  2 TreePanel tp = (TreePanel) frames[t];
1248   
1249  2 if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
1250    {
1251  2 JalviewModel.Tree tree = new JalviewModel.Tree();
1252  2 tree.setTitle(tp.getTitle());
1253  2 tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
1254  2 tree.setNewick(tp.getTree().print());
1255  2 tree.setThreshold(tp.getTreeCanvas().getThreshold());
1256   
1257  2 tree.setFitToWindow(tp.fitToWindow.getState());
1258  2 tree.setFontName(tp.getTreeFont().getName());
1259  2 tree.setFontSize(tp.getTreeFont().getSize());
1260  2 tree.setFontStyle(tp.getTreeFont().getStyle());
1261  2 tree.setMarkUnlinked(tp.placeholdersMenu.getState());
1262   
1263  2 tree.setShowBootstrap(tp.bootstrapMenu.getState());
1264  2 tree.setShowDistances(tp.distanceMenu.getState());
1265   
1266  2 tree.setHeight(tp.getHeight());
1267  2 tree.setWidth(tp.getWidth());
1268  2 tree.setXpos(tp.getX());
1269  2 tree.setYpos(tp.getY());
1270  2 tree.setId(makeHashCode(tp, null));
1271  2 tree.setLinkToAllViews(
1272    tp.getTreeCanvas().isApplyToAllViews());
1273   
1274    // jms.addTree(tree);
1275  2 object.getTree().add(tree);
1276    }
1277    }
1278    }
1279    }
1280    }
1281   
1282    /*
1283    * save PCA viewers
1284    */
1285  45 if (!storeDS && Desktop.desktop != null)
1286    {
1287  29 for (JInternalFrame frame : Desktop.desktop.getAllFrames())
1288    {
1289  91 if (frame instanceof PCAPanel)
1290    {
1291  5 PCAPanel panel = (PCAPanel) frame;
1292  5 if (panel.getAlignViewport().getAlignment() == jal)
1293    {
1294  1 savePCA(panel, object);
1295    }
1296    }
1297    }
1298    }
1299   
1300    // SAVE ANNOTATIONS
1301    /**
1302    * store forward refs from an annotationRow to any groups
1303    */
1304  45 IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<>();
1305  45 if (storeDS)
1306    {
1307  16 for (SequenceI sq : jal.getSequences())
1308    {
1309    // Store annotation on dataset sequences only
1310  242 AlignmentAnnotation[] aa = sq.getAnnotation();
1311  242 if (aa != null && aa.length > 0)
1312    {
1313  80 storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
1314    vamsasSet);
1315    }
1316    }
1317    }
1318    else
1319    {
1320  29 if (jal.getAlignmentAnnotation() != null)
1321    {
1322    // Store the annotation shown on the alignment.
1323  29 AlignmentAnnotation[] aa = jal.getAlignmentAnnotation();
1324  29 storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
1325    vamsasSet);
1326    }
1327    }
1328    // SAVE GROUPS
1329  45 if (jal.getGroups() != null)
1330    {
1331  45 JGroup[] groups = new JGroup[jal.getGroups().size()];
1332  45 int i = -1;
1333  45 for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
1334    {
1335  13 JGroup jGroup = new JGroup();
1336  13 groups[++i] = jGroup;
1337   
1338  13 jGroup.setStart(sg.getStartRes());
1339  13 jGroup.setEnd(sg.getEndRes());
1340  13 jGroup.setName(sg.getName());
1341  13 if (groupRefs.containsKey(sg))
1342    {
1343    // group has references so set its ID field
1344  6 jGroup.setId(groupRefs.get(sg));
1345    }
1346  13 ColourSchemeI colourScheme = sg.getColourScheme();
1347  13 if (colourScheme != null)
1348    {
1349  7 ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
1350  7 if (groupColourScheme.conservationApplied())
1351    {
1352  2 jGroup.setConsThreshold(groupColourScheme.getConservationInc());
1353   
1354  2 if (colourScheme instanceof jalview.schemes.UserColourScheme)
1355    {
1356  0 jGroup.setColour(
1357    setUserColourScheme(colourScheme, userColours,
1358    object));
1359    }
1360    else
1361    {
1362  2 jGroup.setColour(colourScheme.getSchemeName());
1363    }
1364    }
1365  5 else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
1366    {
1367  1 jGroup.setColour("AnnotationColourGradient");
1368  1 jGroup.setAnnotationColours(constructAnnotationColours(
1369    (jalview.schemes.AnnotationColourGradient) colourScheme,
1370    userColours, object));
1371    }
1372  4 else if (colourScheme instanceof jalview.schemes.UserColourScheme)
1373    {
1374  0 jGroup.setColour(
1375    setUserColourScheme(colourScheme, userColours, object));
1376    }
1377    else
1378    {
1379  4 jGroup.setColour(colourScheme.getSchemeName());
1380    }
1381   
1382  7 jGroup.setPidThreshold(groupColourScheme.getThreshold());
1383    }
1384   
1385  13 jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
1386  13 jGroup.setDisplayBoxes(sg.getDisplayBoxes());
1387  13 jGroup.setDisplayText(sg.getDisplayText());
1388  13 jGroup.setColourText(sg.getColourText());
1389  13 jGroup.setTextCol1(sg.textColour.getRGB());
1390  13 jGroup.setTextCol2(sg.textColour2.getRGB());
1391  13 jGroup.setTextColThreshold(sg.thresholdTextColour);
1392  13 jGroup.setShowUnconserved(sg.getShowNonconserved());
1393  13 jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
1394  13 jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram());
1395  13 jGroup.setShowSequenceLogo(sg.isShowSequenceLogo());
1396  13 jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
1397  13 for (SequenceI seq : sg.getSequences())
1398    {
1399    // jGroup.addSeq(seqHash(seq));
1400  84 jGroup.getSeq().add(seqHash(seq));
1401    }
1402    }
1403   
1404    //jms.setJGroup(groups);
1405  45 Object group;
1406  45 for (JGroup grp : groups)
1407    {
1408  13 object.getJGroup().add(grp);
1409    }
1410    }
1411  45 if (!storeDS)
1412    {
1413    // /////////SAVE VIEWPORT
1414  29 Viewport view = new Viewport();
1415  29 view.setTitle(ap.alignFrame.getTitle());
1416  29 view.setSequenceSetId(
1417    makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
1418  29 view.setId(av.getViewId());
1419  29 if (av.getCodingComplement() != null)
1420    {
1421  0 view.setComplementId(av.getCodingComplement().getViewId());
1422    }
1423  29 view.setViewName(av.getViewName());
1424  29 view.setGatheredViews(av.isGatherViewsHere());
1425   
1426  29 Rectangle size = ap.av.getExplodedGeometry();
1427  29 Rectangle position = size;
1428  29 if (size == null)
1429    {
1430  19 size = ap.alignFrame.getBounds();
1431  19 if (av.getCodingComplement() != null)
1432    {
1433  0 position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
1434    .getBounds();
1435    }
1436    else
1437    {
1438  19 position = size;
1439    }
1440    }
1441  29 view.setXpos(position.x);
1442  29 view.setYpos(position.y);
1443   
1444  29 view.setWidth(size.width);
1445  29 view.setHeight(size.height);
1446   
1447  29 view.setStartRes(vpRanges.getStartRes());
1448  29 view.setStartSeq(vpRanges.getStartSeq());
1449   
1450  29 if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
1451    {
1452  0 view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
1453    userColours, object));
1454    }
1455  29 else if (av
1456    .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
1457    {
1458  1 AnnotationColourScheme ac = constructAnnotationColours(
1459    (jalview.schemes.AnnotationColourGradient) av
1460    .getGlobalColourScheme(),
1461    userColours, object);
1462   
1463  1 view.setAnnotationColours(ac);
1464  1 view.setBgColour("AnnotationColourGradient");
1465    }
1466    else
1467    {
1468  28 view.setBgColour(ColourSchemeProperty
1469    .getColourName(av.getGlobalColourScheme()));
1470    }
1471   
1472  29 ResidueShaderI vcs = av.getResidueShading();
1473  29 ColourSchemeI cs = av.getGlobalColourScheme();
1474   
1475  29 if (cs != null)
1476    {
1477  5 if (vcs.conservationApplied())
1478    {
1479  2 view.setConsThreshold(vcs.getConservationInc());
1480  2 if (cs instanceof jalview.schemes.UserColourScheme)
1481    {
1482  0 view.setBgColour(setUserColourScheme(cs, userColours, object));
1483    }
1484    }
1485  5 view.setPidThreshold(vcs.getThreshold());
1486    }
1487   
1488  29 view.setConservationSelected(av.getConservationSelected());
1489  29 view.setPidSelected(av.getAbovePIDThreshold());
1490  29 final Font font = av.getFont();
1491  29 view.setFontName(font.getName());
1492  29 view.setFontSize(font.getSize());
1493  29 view.setFontStyle(font.getStyle());
1494  29 view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
1495  29 view.setRenderGaps(av.isRenderGaps());
1496  29 view.setShowAnnotation(av.isShowAnnotation());
1497  29 view.setShowBoxes(av.getShowBoxes());
1498  29 view.setShowColourText(av.getColourText());
1499  29 view.setShowFullId(av.getShowJVSuffix());
1500  29 view.setRightAlignIds(av.isRightAlignIds());
1501  29 view.setShowSequenceFeatures(av.isShowSequenceFeatures());
1502  29 view.setShowText(av.getShowText());
1503  29 view.setShowUnconserved(av.getShowUnconserved());
1504  29 view.setWrapAlignment(av.getWrapAlignment());
1505  29 view.setTextCol1(av.getTextColour().getRGB());
1506  29 view.setTextCol2(av.getTextColour2().getRGB());
1507  29 view.setTextColThreshold(av.getThresholdTextColour());
1508  29 view.setShowConsensusHistogram(av.isShowConsensusHistogram());
1509  29 view.setShowSequenceLogo(av.isShowSequenceLogo());
1510  29 view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
1511  29 view.setShowGroupConsensus(av.isShowGroupConsensus());
1512  29 view.setShowGroupConservation(av.isShowGroupConservation());
1513  29 view.setShowNPfeatureTooltip(av.isShowNPFeats());
1514  29 view.setShowDbRefTooltip(av.isShowDBRefs());
1515  29 view.setFollowHighlight(av.isFollowHighlight());
1516  29 view.setFollowSelection(av.followSelection);
1517  29 view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
1518  29 view.setShowComplementFeatures(av.isShowComplementFeatures());
1519  29 view.setShowComplementFeaturesOnTop(
1520    av.isShowComplementFeaturesOnTop());
1521  29 if (av.getFeaturesDisplayed() != null)
1522    {
1523  11 FeatureSettings fs = new FeatureSettings();
1524   
1525  11 FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
1526    .getFeatureRenderer();
1527  11 String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
1528   
1529  11 Vector<String> settingsAdded = new Vector<>();
1530  11 if (renderOrder != null)
1531    {
1532  11 for (String featureType : renderOrder)
1533    {
1534  213 FeatureSettings.Setting setting = new FeatureSettings.Setting();
1535  213 setting.setType(featureType);
1536   
1537    /*
1538    * save any filter for the feature type
1539    */
1540  213 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1541  213 if (filter != null) {
1542  3 Iterator<FeatureMatcherI> filters = filter.getMatchers().iterator();
1543  3 FeatureMatcherI firstFilter = filters.next();
1544  3 setting.setMatcherSet(Jalview2XML.marshalFilter(
1545    firstFilter, filters, filter.isAnded()));
1546    }
1547   
1548    /*
1549    * save colour scheme for the feature type
1550    */
1551  213 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1552  213 if (!fcol.isSimpleColour())
1553    {
1554  4 setting.setColour(fcol.getMaxColour().getRGB());
1555  4 setting.setMincolour(fcol.getMinColour().getRGB());
1556  4 setting.setMin(fcol.getMin());
1557  4 setting.setMax(fcol.getMax());
1558  4 setting.setColourByLabel(fcol.isColourByLabel());
1559  4 if (fcol.isColourByAttribute())
1560    {
1561  2 String[] attName = fcol.getAttributeName();
1562  2 setting.getAttributeName().add(attName[0]);
1563  2 if (attName.length > 1)
1564    {
1565  1 setting.getAttributeName().add(attName[1]);
1566    }
1567    }
1568  4 setting.setAutoScale(fcol.isAutoScaled());
1569  4 setting.setThreshold(fcol.getThreshold());
1570  4 Color noColour = fcol.getNoColour();
1571  4 if (noColour == null)
1572    {
1573  4 setting.setNoValueColour(NoValueColour.NONE);
1574    }
1575  0 else if (noColour.equals(fcol.getMaxColour()))
1576    {
1577  0 setting.setNoValueColour(NoValueColour.MAX);
1578    }
1579    else
1580    {
1581  0 setting.setNoValueColour(NoValueColour.MIN);
1582    }
1583    // -1 = No threshold, 0 = Below, 1 = Above
1584  4 setting.setThreshstate(fcol.isAboveThreshold() ? 1
1585  3 : (fcol.isBelowThreshold() ? 0 : -1));
1586    }
1587    else
1588    {
1589  209 setting.setColour(fcol.getColour().getRGB());
1590    }
1591   
1592  213 setting.setDisplay(
1593    av.getFeaturesDisplayed().isVisible(featureType));
1594  213 float rorder = fr
1595    .getOrder(featureType);
1596  213 if (rorder > -1)
1597    {
1598  213 setting.setOrder(rorder);
1599    }
1600    /// fs.addSetting(setting);
1601  213 fs.getSetting().add(setting);
1602  213 settingsAdded.addElement(featureType);
1603    }
1604    }
1605   
1606    // is groups actually supposed to be a map here ?
1607  11 Iterator<String> en = fr.getFeatureGroups().iterator();
1608  11 Vector<String> groupsAdded = new Vector<>();
1609  44 while (en.hasNext())
1610    {
1611  33 String grp = en.next();
1612  33 if (groupsAdded.contains(grp))
1613    {
1614  0 continue;
1615    }
1616  33 Group g = new Group();
1617  33 g.setName(grp);
1618  33 g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
1619    .booleanValue());
1620    // fs.addGroup(g);
1621  33 fs.getGroup().add(g);
1622  33 groupsAdded.addElement(grp);
1623    }
1624    // jms.setFeatureSettings(fs);
1625  11 object.setFeatureSettings(fs);
1626    }
1627   
1628  29 if (av.hasHiddenColumns())
1629    {
1630  4 jalview.datamodel.HiddenColumns hidden = av.getAlignment()
1631    .getHiddenColumns();
1632  4 if (hidden == null)
1633    {
1634  0 warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1635    }
1636    else
1637    {
1638  4 Iterator<int[]> hiddenRegions = hidden.iterator();
1639  10 while (hiddenRegions.hasNext())
1640    {
1641  6 int[] region = hiddenRegions.next();
1642  6 HiddenColumns hc = new HiddenColumns();
1643  6 hc.setStart(region[0]);
1644  6 hc.setEnd(region[1]);
1645    // view.addHiddenColumns(hc);
1646  6 view.getHiddenColumns().add(hc);
1647    }
1648    }
1649    }
1650  29 if (calcIdSet.size() > 0)
1651    {
1652  29 for (String calcId : calcIdSet)
1653    {
1654  32 if (calcId.trim().length() > 0)
1655    {
1656  3 CalcIdParam cidp = createCalcIdParam(calcId, av);
1657    // Some calcIds have no parameters.
1658  3 if (cidp != null)
1659    {
1660    // view.addCalcIdParam(cidp);
1661  0 view.getCalcIdParam().add(cidp);
1662    }
1663    }
1664    }
1665    }
1666   
1667    // jms.addViewport(view);
1668  29 object.getViewport().add(view);
1669    }
1670    // object.setJalviewModelSequence(jms);
1671    // object.getVamsasModel().addSequenceSet(vamsasSet);
1672  45 object.getVamsasModel().getSequenceSet().add(vamsasSet);
1673   
1674  45 if (jout != null && fileName != null)
1675    {
1676    // We may not want to write the object to disk,
1677    // eg we can copy the alignViewport to a new view object
1678    // using save and then load
1679  39 try
1680    {
1681  39 fileName = fileName.replace('\\', '/');
1682  39 System.out.println("Writing jar entry " + fileName);
1683  39 JarEntry entry = new JarEntry(fileName);
1684  39 jout.putNextEntry(entry);
1685  35 PrintWriter pout = new PrintWriter(
1686    new OutputStreamWriter(jout, UTF_8));
1687  35 JAXBContext jaxbContext = JAXBContext
1688    .newInstance(JalviewModel.class);
1689  35 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1690   
1691    // output pretty printed
1692    // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
1693  35 jaxbMarshaller.marshal(
1694    new ObjectFactory().createJalviewModel(object), pout);
1695   
1696    // jaxbMarshaller.marshal(object, pout);
1697    // marshaller.marshal(object);
1698  35 pout.flush();
1699  35 jout.closeEntry();
1700    } catch (Exception ex)
1701    {
1702    // TODO: raise error in GUI if marshalling failed.
1703  4 System.err.println("Error writing Jalview project");
1704  4 ex.printStackTrace();
1705    }
1706    }
1707  45 return object;
1708    }
1709   
1710    /**
1711    * Writes PCA viewer attributes and computed values to an XML model object and
1712    * adds it to the JalviewModel. Any exceptions are reported by logging.
1713    */
 
1714  1 toggle protected void savePCA(PCAPanel panel, JalviewModel object)
1715    {
1716  1 try
1717    {
1718  1 PcaViewer viewer = new PcaViewer();
1719  1 viewer.setHeight(panel.getHeight());
1720  1 viewer.setWidth(panel.getWidth());
1721  1 viewer.setXpos(panel.getX());
1722  1 viewer.setYpos(panel.getY());
1723  1 viewer.setTitle(panel.getTitle());
1724  1 PCAModel pcaModel = panel.getPcaModel();
1725  1 viewer.setScoreModelName(pcaModel.getScoreModelName());
1726  1 viewer.setXDim(panel.getSelectedDimensionIndex(X));
1727  1 viewer.setYDim(panel.getSelectedDimensionIndex(Y));
1728  1 viewer.setZDim(panel.getSelectedDimensionIndex(Z));
1729  1 viewer.setBgColour(
1730    panel.getRotatableCanvas().getBackgroundColour().getRGB());
1731  1 viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
1732  1 float[] spMin = panel.getRotatableCanvas().getSeqMin();
1733  1 SeqPointMin spmin = new SeqPointMin();
1734  1 spmin.setXPos(spMin[0]);
1735  1 spmin.setYPos(spMin[1]);
1736  1 spmin.setZPos(spMin[2]);
1737  1 viewer.setSeqPointMin(spmin);
1738  1 float[] spMax = panel.getRotatableCanvas().getSeqMax();
1739  1 SeqPointMax spmax = new SeqPointMax();
1740  1 spmax.setXPos(spMax[0]);
1741  1 spmax.setYPos(spMax[1]);
1742  1 spmax.setZPos(spMax[2]);
1743  1 viewer.setSeqPointMax(spmax);
1744  1 viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
1745  1 viewer.setLinkToAllViews(
1746    panel.getRotatableCanvas().isApplyToAllViews());
1747  1 SimilarityParamsI sp = pcaModel.getSimilarityParameters();
1748  1 viewer.setIncludeGaps(sp.includeGaps());
1749  1 viewer.setMatchGaps(sp.matchGaps());
1750  1 viewer.setIncludeGappedColumns(sp.includeGappedColumns());
1751  1 viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
1752   
1753    /*
1754    * sequence points on display
1755    */
1756  1 for (jalview.datamodel.SequencePoint spt : pcaModel
1757    .getSequencePoints())
1758    {
1759  15 SequencePoint point = new SequencePoint();
1760  15 point.setSequenceRef(seqHash(spt.getSequence()));
1761  15 point.setXPos(spt.coord.x);
1762  15 point.setYPos(spt.coord.y);
1763  15 point.setZPos(spt.coord.z);
1764  15 viewer.getSequencePoint().add(point);
1765    }
1766   
1767    /*
1768    * (end points of) axes on display
1769    */
1770  1 for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
1771    {
1772   
1773  3 Axis axis = new Axis();
1774  3 axis.setXPos(p.x);
1775  3 axis.setYPos(p.y);
1776  3 axis.setZPos(p.z);
1777  3 viewer.getAxis().add(axis);
1778    }
1779   
1780    /*
1781    * raw PCA data (note we are not restoring PCA inputs here -
1782    * alignment view, score model, similarity parameters)
1783    */
1784  1 PcaDataType data = new PcaDataType();
1785  1 viewer.setPcaData(data);
1786  1 PCA pca = pcaModel.getPcaData();
1787   
1788  1 DoubleMatrix pm = new DoubleMatrix();
1789  1 saveDoubleMatrix(pca.getPairwiseScores(), pm);
1790  1 data.setPairwiseMatrix(pm);
1791   
1792  1 DoubleMatrix tm = new DoubleMatrix();
1793  1 saveDoubleMatrix(pca.getTridiagonal(), tm);
1794  1 data.setTridiagonalMatrix(tm);
1795   
1796  1 DoubleMatrix eigenMatrix = new DoubleMatrix();
1797  1 data.setEigenMatrix(eigenMatrix);
1798  1 saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
1799   
1800  1 object.getPcaViewer().add(viewer);
1801    } catch (Throwable t)
1802    {
1803  0 Cache.log.error("Error saving PCA: " + t.getMessage());
1804    }
1805    }
1806   
1807    /**
1808    * Stores values from a matrix into an XML element, including (if present) the
1809    * D or E vectors
1810    *
1811    * @param m
1812    * @param xmlMatrix
1813    * @see #loadDoubleMatrix(DoubleMatrix)
1814    */
 
1815  3 toggle protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
1816    {
1817  3 xmlMatrix.setRows(m.height());
1818  3 xmlMatrix.setColumns(m.width());
1819  48 for (int i = 0; i < m.height(); i++)
1820    {
1821  45 DoubleVector row = new DoubleVector();
1822  720 for (int j = 0; j < m.width(); j++)
1823    {
1824  675 row.getV().add(m.getValue(i, j));
1825    }
1826  45 xmlMatrix.getRow().add(row);
1827    }
1828  3 if (m.getD() != null)
1829    {
1830  2 DoubleVector dVector = new DoubleVector();
1831  2 for (double d : m.getD())
1832    {
1833  30 dVector.getV().add(d);
1834    }
1835  2 xmlMatrix.setD(dVector);
1836    }
1837  3 if (m.getE() != null)
1838    {
1839  2 DoubleVector eVector = new DoubleVector();
1840  2 for (double e : m.getE())
1841    {
1842  30 eVector.getV().add(e);
1843    }
1844  2 xmlMatrix.setE(eVector);
1845    }
1846    }
1847   
1848    /**
1849    * Loads XML matrix data into a new Matrix object, including the D and/or E
1850    * vectors (if present)
1851    *
1852    * @param mData
1853    * @return
1854    * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
1855    */
 
1856  3 toggle protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
1857    {
1858  3 int rows = mData.getRows();
1859  3 double[][] vals = new double[rows][];
1860   
1861  48 for (int i = 0; i < rows; i++)
1862    {
1863  45 List<Double> dVector = mData.getRow().get(i).getV();
1864  45 vals[i] = new double[dVector.size()];
1865  45 int dvi = 0;
1866  45 for (Double d : dVector)
1867    {
1868  675 vals[i][dvi++] = d;
1869    }
1870    }
1871   
1872  3 MatrixI m = new Matrix(vals);
1873   
1874  3 if (mData.getD() != null)
1875    {
1876  2 List<Double> dVector = mData.getD().getV();
1877  2 double[] vec = new double[dVector.size()];
1878  2 int dvi = 0;
1879  2 for (Double d : dVector)
1880    {
1881  30 vec[dvi++] = d;
1882    }
1883  2 m.setD(vec);
1884    }
1885  3 if (mData.getE() != null)
1886    {
1887  2 List<Double> dVector = mData.getE().getV();
1888  2 double[] vec = new double[dVector.size()];
1889  2 int dvi = 0;
1890  2 for (Double d : dVector)
1891    {
1892  30 vec[dvi++] = d;
1893    }
1894  2 m.setE(vec);
1895    }
1896   
1897  3 return m;
1898    }
1899   
1900    /**
1901    * Save any Varna viewers linked to this sequence. Writes an rnaViewer element
1902    * for each viewer, with
1903    * <ul>
1904    * <li>viewer geometry (position, size, split pane divider location)</li>
1905    * <li>index of the selected structure in the viewer (currently shows gapped
1906    * or ungapped)</li>
1907    * <li>the id of the annotation holding RNA secondary structure</li>
1908    * <li>(currently only one SS is shown per viewer, may be more in future)</li>
1909    * </ul>
1910    * Varna viewer state is also written out (in native Varna XML) to separate
1911    * project jar entries. A separate entry is written for each RNA structure
1912    * displayed, with the naming convention
1913    * <ul>
1914    * <li>rna_viewId_sequenceId_annotationId_[gapped|trimmed]</li>
1915    * </ul>
1916    *
1917    * @param jout
1918    * @param jseq
1919    * @param jds
1920    * @param viewIds
1921    * @param ap
1922    * @param storeDataset
1923    */
 
1924  669 toggle protected void saveRnaViewers(JarOutputStream jout, JSeq jseq,
1925    final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
1926    boolean storeDataset)
1927    {
1928  669 if (Desktop.desktop == null)
1929    {
1930  0 return;
1931    }
1932  669 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1933  3128 for (int f = frames.length - 1; f > -1; f--)
1934    {
1935  2459 if (frames[f] instanceof AppVarna)
1936    {
1937  0 AppVarna varna = (AppVarna) frames[f];
1938    /*
1939    * link the sequence to every viewer that is showing it and is linked to
1940    * its alignment panel
1941    */
1942  0 if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel())
1943    {
1944  0 String viewId = varna.getViewId();
1945  0 RnaViewer rna = new RnaViewer();
1946  0 rna.setViewId(viewId);
1947  0 rna.setTitle(varna.getTitle());
1948  0 rna.setXpos(varna.getX());
1949  0 rna.setYpos(varna.getY());
1950  0 rna.setWidth(varna.getWidth());
1951  0 rna.setHeight(varna.getHeight());
1952  0 rna.setDividerLocation(varna.getDividerLocation());
1953  0 rna.setSelectedRna(varna.getSelectedIndex());
1954    // jseq.addRnaViewer(rna);
1955  0 jseq.getRnaViewer().add(rna);
1956   
1957    /*
1958    * Store each Varna panel's state once in the project per sequence.
1959    * First time through only (storeDataset==false)
1960    */
1961    // boolean storeSessions = false;
1962    // String sequenceViewId = viewId + seqsToIds.get(jds);
1963    // if (!storeDataset && !viewIds.contains(sequenceViewId))
1964    // {
1965    // viewIds.add(sequenceViewId);
1966    // storeSessions = true;
1967    // }
1968  0 for (RnaModel model : varna.getModels())
1969    {
1970  0 if (model.seq == jds)
1971    {
1972    /*
1973    * VARNA saves each view (sequence or alignment secondary
1974    * structure, gapped or trimmed) as a separate XML file
1975    */
1976  0 String jarEntryName = rnaSessions.get(model);
1977  0 if (jarEntryName == null)
1978    {
1979   
1980  0 String varnaStateFile = varna.getStateInfo(model.rna);
1981  0 jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
1982  0 copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna");
1983  0 rnaSessions.put(model, jarEntryName);
1984    }
1985  0 SecondaryStructure ss = new SecondaryStructure();
1986  0 String annotationId = varna.getAnnotation(jds).annotationId;
1987  0 ss.setAnnotationId(annotationId);
1988  0 ss.setViewerState(jarEntryName);
1989  0 ss.setGapped(model.gapped);
1990  0 ss.setTitle(model.title);
1991    // rna.addSecondaryStructure(ss);
1992  0 rna.getSecondaryStructure().add(ss);
1993    }
1994    }
1995    }
1996    }
1997    }
1998    }
1999   
2000    /**
2001    * Copy the contents of a file to a new entry added to the output jar
2002    *
2003    * @param jout
2004    * @param infilePath
2005    * @param jarEntryName
2006    * @param msg
2007    * additional identifying info to log to the console
2008    */
 
2009  6 toggle protected void copyFileToJar(JarOutputStream jout, String infilePath,
2010    String jarEntryName, String msg)
2011    {
2012  6 try (InputStream is = new FileInputStream(infilePath))
2013    {
2014  6 File file = new File(infilePath);
2015  6 if (file.exists() && jout != null)
2016    {
2017  6 System.out.println(
2018    "Writing jar entry " + jarEntryName + " (" + msg + ")");
2019  6 jout.putNextEntry(new JarEntry(jarEntryName));
2020  6 copyAll(is, jout);
2021  6 jout.closeEntry();
2022    // dis = new DataInputStream(new FileInputStream(file));
2023    // byte[] data = new byte[(int) file.length()];
2024    // dis.readFully(data);
2025    // writeJarEntry(jout, jarEntryName, data);
2026    }
2027    } catch (Exception ex)
2028    {
2029  0 ex.printStackTrace();
2030    }
2031    }
2032   
2033    /**
2034    * Copies input to output, in 4K buffers; handles any data (text or binary)
2035    *
2036    * @param in
2037    * @param out
2038    * @throws IOException
2039    */
 
2040  21 toggle protected void copyAll(InputStream in, OutputStream out)
2041    throws IOException
2042    {
2043  21 byte[] buffer = new byte[4096];
2044  21 int bytesRead = 0;
2045  ? while ((bytesRead = in.read(buffer)) != -1)
2046    {
2047  804 out.write(buffer, 0, bytesRead);
2048    }
2049    }
2050   
2051    /**
2052    * Save the state of a structure viewer
2053    *
2054    * @param ap
2055    * @param jds
2056    * @param pdb
2057    * the archive XML element under which to save the state
2058    * @param entry
2059    * @param viewIds
2060    * @param matchedFile
2061    * @param viewFrame
2062    * @return
2063    */
 
2064  48 toggle protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
2065    Pdbids pdb, PDBEntry entry, List<String> viewIds,
2066    String matchedFile, StructureViewerBase viewFrame)
2067    {
2068  48 final AAStructureBindingModel bindingModel = viewFrame.getBinding();
2069   
2070    /*
2071    * Look for any bindings for this viewer to the PDB file of interest
2072    * (including part matches excluding chain id)
2073    */
2074  96 for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
2075    {
2076  48 final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
2077  48 final String pdbId = pdbentry.getId();
2078  48 if (!pdbId.equals(entry.getId())
2079    && !(entry.getId().length() > 4 && entry.getId().toLowerCase()
2080    .startsWith(pdbId.toLowerCase())))
2081    {
2082    /*
2083    * not interested in a binding to a different PDB entry here
2084    */
2085  16 continue;
2086    }
2087  32 if (matchedFile == null)
2088    {
2089  32 matchedFile = pdbentry.getFile();
2090    }
2091  0 else if (!matchedFile.equals(pdbentry.getFile()))
2092    {
2093  0 Cache.log.warn(
2094    "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
2095    + pdbentry.getFile());
2096    }
2097    // record the
2098    // file so we
2099    // can get at it if the ID
2100    // match is ambiguous (e.g.
2101    // 1QIP==1qipA)
2102   
2103  96 for (int smap = 0; smap < viewFrame.getBinding()
2104    .getSequence()[peid].length; smap++)
2105    {
2106    // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
2107  64 if (jds == viewFrame.getBinding().getSequence()[peid][smap])
2108    {
2109  20 StructureState state = new StructureState();
2110  20 state.setVisible(true);
2111  20 state.setXpos(viewFrame.getX());
2112  20 state.setYpos(viewFrame.getY());
2113  20 state.setWidth(viewFrame.getWidth());
2114  20 state.setHeight(viewFrame.getHeight());
2115  20 final String viewId = viewFrame.getViewId();
2116  20 state.setViewId(viewId);
2117  20 state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
2118  20 state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap));
2119  20 state.setColourByJmol(viewFrame.isColouredByViewer());
2120  20 state.setType(viewFrame.getViewerType().toString());
2121    // pdb.addStructureState(state);
2122  20 pdb.getStructureState().add(state);
2123    }
2124    }
2125    }
2126  48 return matchedFile;
2127    }
2128   
2129    /**
2130    * Populates the AnnotationColourScheme xml for save. This captures the
2131    * settings of the options in the 'Colour by Annotation' dialog.
2132    *
2133    * @param acg
2134    * @param userColours
2135    * @param jm
2136    * @return
2137    */
 
2138  2 toggle private AnnotationColourScheme constructAnnotationColours(
2139    AnnotationColourGradient acg, List<UserColourScheme> userColours,
2140    JalviewModel jm)
2141    {
2142  2 AnnotationColourScheme ac = new AnnotationColourScheme();
2143  2 ac.setAboveThreshold(acg.getAboveThreshold());
2144  2 ac.setThreshold(acg.getAnnotationThreshold());
2145    // 2.10.2 save annotationId (unique) not annotation label
2146  2 ac.setAnnotation(acg.getAnnotation().annotationId);
2147  2 if (acg.getBaseColour() instanceof UserColourScheme)
2148    {
2149  0 ac.setColourScheme(
2150    setUserColourScheme(acg.getBaseColour(), userColours, jm));
2151    }
2152    else
2153    {
2154  2 ac.setColourScheme(
2155    ColourSchemeProperty.getColourName(acg.getBaseColour()));
2156    }
2157   
2158  2 ac.setMaxColour(acg.getMaxColour().getRGB());
2159  2 ac.setMinColour(acg.getMinColour().getRGB());
2160  2 ac.setPerSequence(acg.isSeqAssociated());
2161  2 ac.setPredefinedColours(acg.isPredefinedColours());
2162  2 return ac;
2163    }
2164   
 
2165  109 toggle private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
2166    IdentityHashMap<SequenceGroup, String> groupRefs,
2167    AlignmentViewport av, Set<String> calcIdSet, boolean storeDS,
2168    SequenceSet vamsasSet)
2169    {
2170   
2171  466 for (int i = 0; i < aa.length; i++)
2172    {
2173  357 Annotation an = new Annotation();
2174   
2175  357 AlignmentAnnotation annotation = aa[i];
2176  357 if (annotation.annotationId != null)
2177    {
2178  357 annotationIds.put(annotation.annotationId, annotation);
2179    }
2180   
2181  357 an.setId(annotation.annotationId);
2182   
2183  357 an.setVisible(annotation.visible);
2184   
2185  357 an.setDescription(annotation.description);
2186   
2187  357 if (annotation.sequenceRef != null)
2188    {
2189    // 2.9 JAL-1781 xref on sequence id rather than name
2190  219 an.setSequenceRef(seqsToIds.get(annotation.sequenceRef));
2191    }
2192  357 if (annotation.groupRef != null)
2193    {
2194  6 String groupIdr = groupRefs.get(annotation.groupRef);
2195  6 if (groupIdr == null)
2196    {
2197    // make a locally unique String
2198  6 groupRefs.put(annotation.groupRef,
2199    groupIdr = ("" + System.currentTimeMillis()
2200    + annotation.groupRef.getName()
2201    + groupRefs.size()));
2202    }
2203  6 an.setGroupRef(groupIdr.toString());
2204    }
2205   
2206    // store all visualization attributes for annotation
2207  357 an.setGraphHeight(annotation.graphHeight);
2208  357 an.setCentreColLabels(annotation.centreColLabels);
2209  357 an.setScaleColLabels(annotation.scaleColLabel);
2210  357 an.setShowAllColLabels(annotation.showAllColLabels);
2211  357 an.setBelowAlignment(annotation.belowAlignment);
2212   
2213  357 if (annotation.graph > 0)
2214    {
2215  198 an.setGraph(true);
2216  198 an.setGraphType(annotation.graph);
2217  198 an.setGraphGroup(annotation.graphGroup);
2218  198 if (annotation.getThreshold() != null)
2219    {
2220  30 ThresholdLine line = new ThresholdLine();
2221  30 line.setLabel(annotation.getThreshold().label);
2222  30 line.setValue(annotation.getThreshold().value);
2223  30 line.setColour(annotation.getThreshold().colour.getRGB());
2224  30 an.setThresholdLine(line);
2225    }
2226    }
2227    else
2228    {
2229  159 an.setGraph(false);
2230    }
2231   
2232  357 an.setLabel(annotation.label);
2233   
2234  357 if (annotation == av.getAlignmentQualityAnnot()
2235    || annotation == av.getAlignmentConservationAnnotation()
2236    || annotation == av.getAlignmentConsensusAnnotation()
2237    || annotation.autoCalculated)
2238    {
2239    // new way of indicating autocalculated annotation -
2240  117 an.setAutoCalculated(annotation.autoCalculated);
2241    }
2242  357 if (annotation.hasScore())
2243    {
2244  139 an.setScore(annotation.getScore());
2245    }
2246   
2247  357 if (annotation.getCalcId() != null)
2248    {
2249  333 calcIdSet.add(annotation.getCalcId());
2250  333 an.setCalcId(annotation.getCalcId());
2251    }
2252  357 if (annotation.hasProperties())
2253    {
2254  0 for (String pr : annotation.getProperties())
2255    {
2256  0 jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property();
2257  0 prop.setName(pr);
2258  0 prop.setValue(annotation.getProperty(pr));
2259    // an.addProperty(prop);
2260  0 an.getProperty().add(prop);
2261    }
2262    }
2263   
2264  357 AnnotationElement ae;
2265  357 if (annotation.annotations != null)
2266    {
2267  357 an.setScoreOnly(false);
2268  47141 for (int a = 0; a < annotation.annotations.length; a++)
2269    {
2270  46784 if ((annotation == null) || (annotation.annotations[a] == null))
2271    {
2272  7457 continue;
2273    }
2274   
2275  39327 ae = new AnnotationElement();
2276  39327 if (annotation.annotations[a].description != null)
2277    {
2278  34908 ae.setDescription(annotation.annotations[a].description);
2279    }
2280  39327 if (annotation.annotations[a].displayCharacter != null)
2281    {
2282  35830 ae.setDisplayCharacter(
2283    annotation.annotations[a].displayCharacter);
2284    }
2285   
2286  39327 if (!Float.isNaN(annotation.annotations[a].value))
2287    {
2288  37623 ae.setValue(annotation.annotations[a].value);
2289    }
2290   
2291  39327 ae.setPosition(a);
2292  39327 if (annotation.annotations[a].secondaryStructure > ' ')
2293    {
2294  6484 ae.setSecondaryStructure(
2295    annotation.annotations[a].secondaryStructure + "");
2296    }
2297   
2298  39327 if (annotation.annotations[a].colour != null
2299    && annotation.annotations[a].colour != java.awt.Color.black)
2300    {
2301  20104 ae.setColour(annotation.annotations[a].colour.getRGB());
2302    }
2303   
2304    // an.addAnnotationElement(ae);
2305  39327 an.getAnnotationElement().add(ae);
2306  39327 if (annotation.autoCalculated)
2307    {
2308    // only write one non-null entry into the annotation row -
2309    // sufficient to get the visualization attributes necessary to
2310    // display data
2311  18113 continue;
2312    }
2313    }
2314    }
2315    else
2316    {
2317  0 an.setScoreOnly(true);
2318    }
2319  357 if (!storeDS || (storeDS && !annotation.autoCalculated))
2320    {
2321    // skip autocalculated annotation - these are only provided for
2322    // alignments
2323    // vamsasSet.addAnnotation(an);
2324  357 vamsasSet.getAnnotation().add(an);
2325    }
2326    }
2327   
2328    }
2329   
 
2330  3 toggle private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
2331    {
2332  3 AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
2333  3 if (settings != null)
2334    {
2335  0 CalcIdParam vCalcIdParam = new CalcIdParam();
2336  0 vCalcIdParam.setCalcId(calcId);
2337    // vCalcIdParam.addServiceURL(settings.getServiceURI());
2338  0 vCalcIdParam.getServiceURL().add(settings.getServiceURI());
2339    // generic URI allowing a third party to resolve another instance of the
2340    // service used for this calculation
2341  0 for (String url : settings.getServiceURLs())
2342    {
2343    // vCalcIdParam.addServiceURL(urls);
2344  0 vCalcIdParam.getServiceURL().add(url);
2345    }
2346  0 vCalcIdParam.setVersion("1.0");
2347  0 if (settings.getPreset() != null)
2348    {
2349  0 WsParamSetI setting = settings.getPreset();
2350  0 vCalcIdParam.setName(setting.getName());
2351  0 vCalcIdParam.setDescription(setting.getDescription());
2352    }
2353    else
2354    {
2355  0 vCalcIdParam.setName("");
2356  0 vCalcIdParam.setDescription("Last used parameters");
2357    }
2358    // need to be able to recover 1) settings 2) user-defined presets or
2359    // recreate settings from preset 3) predefined settings provided by
2360    // service - or settings that can be transferred (or discarded)
2361  0 vCalcIdParam.setParameters(
2362    settings.getWsParamFile().replace("\n", "|\\n|"));
2363  0 vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
2364    // todo - decide if updateImmediately is needed for any projects.
2365   
2366  0 return vCalcIdParam;
2367    }
2368  3 return null;
2369    }
2370   
 
2371  0 toggle private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
2372    AlignViewport av)
2373    {
2374  0 if (calcIdParam.getVersion().equals("1.0"))
2375    {
2376  0 final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]);
2377  0 Jws2Instance service = Jws2Discoverer.getDiscoverer()
2378    .getPreferredServiceFor(calcIds);
2379  0 if (service != null)
2380    {
2381  0 WsParamSetI parmSet = null;
2382  0 try
2383    {
2384  0 parmSet = service.getParamStore().parseServiceParameterFile(
2385    calcIdParam.getName(), calcIdParam.getDescription(),
2386    calcIds,
2387    calcIdParam.getParameters().replace("|\\n|", "\n"));
2388    } catch (IOException x)
2389    {
2390  0 warn("Couldn't parse parameter data for "
2391    + calcIdParam.getCalcId(), x);
2392  0 return false;
2393    }
2394  0 List<ArgumentI> argList = null;
2395  0 if (calcIdParam.getName().length() > 0)
2396    {
2397  0 parmSet = service.getParamStore()
2398    .getPreset(calcIdParam.getName());
2399  0 if (parmSet != null)
2400    {
2401    // TODO : check we have a good match with settings in AACon -
2402    // otherwise we'll need to create a new preset
2403    }
2404    }
2405    else
2406    {
2407  0 argList = parmSet.getArguments();
2408  0 parmSet = null;
2409    }
2410  0 AAConSettings settings = new AAConSettings(
2411    calcIdParam.isAutoUpdate(), service, parmSet, argList);
2412  0 av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
2413    calcIdParam.isNeedsUpdate());
2414  0 return true;
2415    }
2416    else
2417    {
2418  0 warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
2419  0 return false;
2420    }
2421    }
2422  0 throw new Error(MessageManager.formatMessage(
2423    "error.unsupported_version_calcIdparam", new Object[]
2424    { calcIdParam.toString() }));
2425    }
2426   
2427    /**
2428    * External mapping between jalview objects and objects yielding a valid and
2429    * unique object ID string. This is null for normal Jalview project IO, but
2430    * non-null when a jalview project is being read or written as part of a
2431    * vamsas session.
2432    */
2433    IdentityHashMap jv2vobj = null;
2434   
2435    /**
2436    * Construct a unique ID for jvobj using either existing bindings or if none
2437    * exist, the result of the hashcode call for the object.
2438    *
2439    * @param jvobj
2440    * jalview data object
2441    * @return unique ID for referring to jvobj
2442    */
 
2443  607 toggle private String makeHashCode(Object jvobj, String altCode)
2444    {
2445  607 if (jv2vobj != null)
2446    {
2447  0 Object id = jv2vobj.get(jvobj);
2448  0 if (id != null)
2449    {
2450  0 return id.toString();
2451    }
2452    // check string ID mappings
2453  0 if (jvids2vobj != null && jvobj instanceof String)
2454    {
2455  0 id = jvids2vobj.get(jvobj);
2456    }
2457  0 if (id != null)
2458    {
2459  0 return id.toString();
2460    }
2461    // give up and warn that something has gone wrong
2462  0 warn("Cannot find ID for object in external mapping : " + jvobj);
2463    }
2464  607 return altCode;
2465    }
2466   
2467    /**
2468    * return local jalview object mapped to ID, if it exists
2469    *
2470    * @param idcode
2471    * (may be null)
2472    * @return null or object bound to idcode
2473    */
 
2474  8 toggle private Object retrieveExistingObj(String idcode)
2475    {
2476  8 if (idcode != null && vobj2jv != null)
2477    {
2478  0 return vobj2jv.get(idcode);
2479    }
2480  8 return null;
2481    }
2482   
2483    /**
2484    * binding from ID strings from external mapping table to jalview data model
2485    * objects.
2486    */
2487    private Hashtable vobj2jv;
2488   
 
2489  496 toggle private Sequence createVamsasSequence(String id, SequenceI jds)
2490    {
2491  496 return createVamsasSequence(true, id, jds, null);
2492    }
2493   
 
2494  496 toggle private Sequence createVamsasSequence(boolean recurse, String id,
2495    SequenceI jds, SequenceI parentseq)
2496    {
2497  496 Sequence vamsasSeq = new Sequence();
2498  496 vamsasSeq.setId(id);
2499  496 vamsasSeq.setName(jds.getName());
2500  496 vamsasSeq.setSequence(jds.getSequenceAsString());
2501  496 vamsasSeq.setDescription(jds.getDescription());
2502  496 List<DBRefEntry> dbrefs = null;
2503  496 if (jds.getDatasetSequence() != null)
2504    {
2505  254 vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
2506    }
2507    else
2508    {
2509    // seqId==dsseqid so we can tell which sequences really are
2510    // dataset sequences only
2511  242 vamsasSeq.setDsseqid(id);
2512  242 dbrefs = jds.getDBRefs();
2513  242 if (parentseq == null)
2514    {
2515  242 parentseq = jds;
2516    }
2517    }
2518   
2519    /*
2520    * save any dbrefs; special subclass GeneLocus is flagged as 'locus'
2521    */
2522  496 if (dbrefs != null)
2523    {
2524  393 for (int d = 0, nd = dbrefs.size(); d < nd; d++)
2525    {
2526  234 DBRef dbref = new DBRef();
2527  234 DBRefEntry ref = dbrefs.get(d);
2528  234 dbref.setSource(ref.getSource());
2529  234 dbref.setVersion(ref.getVersion());
2530  234 dbref.setAccessionId(ref.getAccessionId());
2531  234 if (ref instanceof GeneLocus)
2532    {
2533  1 dbref.setLocus(true);
2534    }
2535  234 if (ref.hasMap())
2536    {
2537  2 Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
2538    jds, recurse);
2539  2 dbref.setMapping(mp);
2540    }
2541  234 vamsasSeq.getDBRef().add(dbref);
2542    }
2543    }
2544  496 return vamsasSeq;
2545    }
2546   
 
2547  2 toggle private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
2548    SequenceI parentseq, SequenceI jds, boolean recurse)
2549    {
2550  2 Mapping mp = null;
2551  2 if (jmp.getMap() != null)
2552    {
2553  2 mp = new Mapping();
2554   
2555  2 jalview.util.MapList mlst = jmp.getMap();
2556  2 List<int[]> r = mlst.getFromRanges();
2557  2 for (int[] range : r)
2558    {
2559  2 MapListFrom mfrom = new MapListFrom();
2560  2 mfrom.setStart(range[0]);
2561  2 mfrom.setEnd(range[1]);
2562    // mp.addMapListFrom(mfrom);
2563  2 mp.getMapListFrom().add(mfrom);
2564    }
2565  2 r = mlst.getToRanges();
2566  2 for (int[] range : r)
2567    {
2568  2 MapListTo mto = new MapListTo();
2569  2 mto.setStart(range[0]);
2570  2 mto.setEnd(range[1]);
2571    // mp.addMapListTo(mto);
2572  2 mp.getMapListTo().add(mto);
2573    }
2574  2 mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
2575  2 mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
2576  2 if (jmp.getTo() != null)
2577    {
2578    // MappingChoice mpc = new MappingChoice();
2579   
2580    // check/create ID for the sequence referenced by getTo()
2581   
2582  1 String jmpid = "";
2583  1 SequenceI ps = null;
2584  1 if (parentseq != jmp.getTo()
2585    && parentseq.getDatasetSequence() != jmp.getTo())
2586    {
2587    // chaining dbref rather than a handshaking one
2588  1 jmpid = seqHash(ps = jmp.getTo());
2589    }
2590    else
2591    {
2592  0 jmpid = seqHash(ps = parentseq);
2593    }
2594    // mpc.setDseqFor(jmpid);
2595  1 mp.setDseqFor(jmpid);
2596  1 if (!seqRefIds.containsKey(jmpid))
2597    {
2598  0 jalview.bin.Cache.log.debug("creatign new DseqFor ID");
2599  0 seqRefIds.put(jmpid, ps);
2600    }
2601    else
2602    {
2603  1 jalview.bin.Cache.log.debug("reusing DseqFor ID");
2604    }
2605   
2606    // mp.setMappingChoice(mpc);
2607    }
2608    }
2609  2 return mp;
2610    }
2611   
 
2612  0 toggle String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
2613    List<UserColourScheme> userColours, JalviewModel jm)
2614    {
2615  0 String id = null;
2616  0 jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
2617  0 boolean newucs = false;
2618  0 if (!userColours.contains(ucs))
2619    {
2620  0 userColours.add(ucs);
2621  0 newucs = true;
2622    }
2623  0 id = "ucs" + userColours.indexOf(ucs);
2624  0 if (newucs)
2625    {
2626    // actually create the scheme's entry in the XML model
2627  0 java.awt.Color[] colours = ucs.getColours();
2628  0 UserColours uc = new UserColours();
2629    // UserColourScheme jbucs = new UserColourScheme();
2630  0 JalviewUserColours jbucs = new JalviewUserColours();
2631   
2632  0 for (int i = 0; i < colours.length; i++)
2633    {
2634  0 Colour col = new Colour();
2635  0 col.setName(ResidueProperties.aa[i]);
2636  0 col.setRGB(jalview.util.Format.getHexString(colours[i]));
2637    // jbucs.addColour(col);
2638  0 jbucs.getColour().add(col);
2639    }
2640  0 if (ucs.getLowerCaseColours() != null)
2641    {
2642  0 colours = ucs.getLowerCaseColours();
2643  0 for (int i = 0; i < colours.length; i++)
2644    {
2645  0 Colour col = new Colour();
2646  0 col.setName(ResidueProperties.aa[i].toLowerCase());
2647  0 col.setRGB(jalview.util.Format.getHexString(colours[i]));
2648    // jbucs.addColour(col);
2649  0 jbucs.getColour().add(col);
2650    }
2651    }
2652   
2653  0 uc.setId(id);
2654  0 uc.setUserColourScheme(jbucs);
2655    // jm.addUserColours(uc);
2656  0 jm.getUserColours().add(uc);
2657    }
2658   
2659  0 return id;
2660    }
2661   
 
2662  0 toggle jalview.schemes.UserColourScheme getUserColourScheme(
2663    JalviewModel jm, String id)
2664    {
2665  0 List<UserColours> uc = jm.getUserColours();
2666  0 UserColours colours = null;
2667    /*
2668    for (int i = 0; i < uc.length; i++)
2669    {
2670    if (uc[i].getId().equals(id))
2671    {
2672    colours = uc[i];
2673    break;
2674    }
2675    }
2676    */
2677  0 for (UserColours c : uc)
2678    {
2679  0 if (c.getId().equals(id))
2680    {
2681  0 colours = c;
2682  0 break;
2683    }
2684    }
2685   
2686  0 java.awt.Color[] newColours = new java.awt.Color[24];
2687   
2688  0 for (int i = 0; i < 24; i++)
2689    {
2690  0 newColours[i] = new java.awt.Color(Integer.parseInt(
2691    // colours.getUserColourScheme().getColour(i).getRGB(), 16));
2692    colours.getUserColourScheme().getColour().get(i).getRGB(),
2693    16));
2694    }
2695   
2696  0 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
2697    newColours);
2698   
2699  0 if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24)
2700    {
2701  0 newColours = new java.awt.Color[23];
2702  0 for (int i = 0; i < 23; i++)
2703    {
2704  0 newColours[i] = new java.awt.Color(Integer.parseInt(
2705    colours.getUserColourScheme().getColour().get(i + 24)
2706    .getRGB(),
2707    16));
2708    }
2709  0 ucs.setLowerCaseColours(newColours);
2710    }
2711   
2712  0 return ucs;
2713    }
2714   
2715    /**
2716    * contains last error message (if any) encountered by XML loader.
2717    */
2718    String errorMessage = null;
2719   
2720    /**
2721    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
2722    * exceptions are raised during project XML parsing
2723    */
2724    public boolean attemptversion1parse = false;
2725   
2726    /**
2727    * Load a jalview project archive from a jar file
2728    *
2729    * @param file
2730    * - HTTP URL or filename
2731    */
 
2732  21 toggle public AlignFrame loadJalviewAlign(final Object file)
2733    {
2734   
2735  21 jalview.gui.AlignFrame af = null;
2736   
2737  21 try
2738    {
2739    // create list to store references for any new Jmol viewers created
2740  21 newStructureViewers = new Vector<>();
2741    // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
2742    // Workaround is to make sure caller implements the JarInputStreamProvider
2743    // interface
2744    // so we can re-open the jar input stream for each entry.
2745   
2746  21 jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
2747  21 af = loadJalviewAlign(jprovider);
2748  21 if (af != null)
2749    {
2750  21 af.setMenusForViewport();
2751    }
2752    } catch (MalformedURLException e)
2753    {
2754  0 errorMessage = "Invalid URL format for '" + file + "'";
2755  0 reportErrors();
2756    } finally
2757    {
2758  21 try
2759    {
2760  21 SwingUtilities.invokeAndWait(new Runnable()
2761    {
 
2762  21 toggle @Override
2763    public void run()
2764    {
2765  21 setLoadingFinishedForNewStructureViewers();
2766    }
2767    });
2768    } catch (Exception x)
2769    {
2770  0 System.err.println("Error loading alignment: " + x.getMessage());
2771    }
2772    }
2773  21 return af;
2774    }
2775   
 
2776  21 toggle @SuppressWarnings("unused")
2777    private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException {
2778   
2779    // BH 2018 allow for bytes already attached to File object
2780  21 try {
2781  21 String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString());
2782  21 byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
2783    : null;
2784  21 URL url = null;
2785  21 errorMessage = null;
2786  21 uniqueSetSuffix = null;
2787  21 seqRefIds = null;
2788  21 viewportsAdded.clear();
2789  21 frefedSequence = null;
2790   
2791  21 if (file.startsWith("http://")) {
2792  0 url = new URL(file);
2793    }
2794  21 final URL _url = url;
2795  21 return new jarInputStreamProvider() {
2796   
 
2797  125 toggle @Override
2798    public JarInputStream getJarInputStream() throws IOException {
2799  125 if (bytes != null) {
2800    // System.out.println("Jalview2XML: opening byte jarInputStream for bytes.length=" + bytes.length);
2801  0 return new JarInputStream(new ByteArrayInputStream(bytes));
2802    }
2803  125 if (_url != null) {
2804    // System.out.println("Jalview2XML: opening url jarInputStream for " + _url);
2805  0 return new JarInputStream(_url.openStream());
2806    } else {
2807    // System.out.println("Jalview2XML: opening file jarInputStream for " + file);
2808  125 return new JarInputStream(new FileInputStream(file));
2809    }
2810    }
2811   
 
2812  21 toggle @Override
2813    public String getFilename() {
2814  21 return file;
2815    }
2816    };
2817    } catch (IOException e) {
2818  0 e.printStackTrace();
2819  0 return null;
2820    }
2821    }
2822   
2823    /**
2824    * Recover jalview session from a jalview project archive. Caller may
2825    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
2826    * themselves. Any null fields will be initialised with default values,
2827    * non-null fields are left alone.
2828    *
2829    * @param jprovider
2830    * @return
2831    */
 
2832  21 toggle public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
2833    {
2834  21 errorMessage = null;
2835  21 if (uniqueSetSuffix == null)
2836    {
2837  21 uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
2838    }
2839  21 if (seqRefIds == null)
2840    {
2841  21 initSeqRefs();
2842    }
2843  21 AlignFrame af = null, _af = null;
2844  21 IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
2845  21 Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
2846  21 final String file = jprovider.getFilename();
2847  21 try
2848    {
2849  21 JarInputStream jin = null;
2850  21 JarEntry jarentry = null;
2851  21 int entryCount = 1;
2852   
2853  21 do
2854    {
2855  113 jin = jprovider.getJarInputStream();
2856  534 for (int i = 0; i < entryCount; i++)
2857    {
2858  421 jarentry = jin.getNextJarEntry();
2859    }
2860   
2861  113 if (jarentry != null && jarentry.getName().endsWith(".xml"))
2862    {
2863  80 JAXBContext jc = JAXBContext
2864    .newInstance("jalview.xml.binding.jalview");
2865  80 XMLStreamReader streamReader = XMLInputFactory.newInstance()
2866    .createXMLStreamReader(jin);
2867  80 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
2868  80 JAXBElement<JalviewModel> jbe = um
2869    .unmarshal(streamReader, JalviewModel.class);
2870  80 JalviewModel object = jbe.getValue();
2871   
2872  80 if (true) // !skipViewport(object))
2873    {
2874  80 _af = loadFromObject(object, file, true, jprovider);
2875  80 if (_af != null && object.getViewport().size() > 0)
2876    // getJalviewModelSequence().getViewportCount() > 0)
2877    {
2878  64 if (af == null)
2879    {
2880    // store a reference to the first view
2881  21 af = _af;
2882    }
2883  64 if (_af.getViewport().isGatherViewsHere())
2884    {
2885    // if this is a gathered view, keep its reference since
2886    // after gathering views, only this frame will remain
2887  12 af = _af;
2888  12 gatherToThisFrame.put(_af.getViewport().getSequenceSetId(),
2889    _af);
2890    }
2891    // Save dataset to register mappings once all resolved
2892  64 importedDatasets.put(
2893    af.getViewport().getAlignment().getDataset(),
2894    af.getViewport().getAlignment().getDataset());
2895    }
2896    }
2897  80 entryCount++;
2898    }
2899  33 else if (jarentry != null)
2900    {
2901    // Some other file here.
2902  12 entryCount++;
2903    }
2904  113 } while (jarentry != null);
2905  21 resolveFrefedSequences();
2906    } catch (IOException ex)
2907    {
2908  0 ex.printStackTrace();
2909  0 errorMessage = "Couldn't locate Jalview XML file : " + file;
2910  0 System.err.println(
2911    "Exception whilst loading jalview XML file : " + ex + "\n");
2912    } catch (Exception ex)
2913    {
2914  0 System.err.println("Parsing as Jalview Version 2 file failed.");
2915  0 ex.printStackTrace(System.err);
2916  0 if (attemptversion1parse)
2917    {
2918    // used to attempt to parse as V1 castor-generated xml
2919    }
2920  0 if (Desktop.instance != null)
2921    {
2922  0 Desktop.instance.stopLoading();
2923    }
2924  0 if (af != null)
2925    {
2926  0 System.out.println("Successfully loaded archive file");
2927  0 return af;
2928    }
2929  0 ex.printStackTrace();
2930   
2931  0 System.err.println(
2932    "Exception whilst loading jalview XML file : " + ex + "\n");
2933    } catch (OutOfMemoryError e)
2934    {
2935    // Don't use the OOM Window here
2936  0 errorMessage = "Out of memory loading jalview XML file";
2937  0 System.err.println("Out of memory whilst loading jalview XML file");
2938  0 e.printStackTrace();
2939    }
2940   
2941    /*
2942    * Regather multiple views (with the same sequence set id) to the frame (if
2943    * any) that is flagged as the one to gather to, i.e. convert them to tabbed
2944    * views instead of separate frames. Note this doesn't restore a state where
2945    * some expanded views in turn have tabbed views - the last "first tab" read
2946    * in will play the role of gatherer for all.
2947    */
2948  21 for (AlignFrame fr : gatherToThisFrame.values())
2949    {
2950  12 Desktop.instance.gatherViews(fr);
2951    }
2952   
2953  21 restoreSplitFrames();
2954  21 for (AlignmentI ds : importedDatasets.keySet())
2955    {
2956  21 if (ds.getCodonFrames() != null)
2957    {
2958  21 StructureSelectionManager
2959    .getStructureSelectionManager(Desktop.instance)
2960    .registerMappings(ds.getCodonFrames());
2961    }
2962    }
2963  21 if (errorMessage != null)
2964    {
2965  0 reportErrors();
2966    }
2967   
2968  21 if (Desktop.instance != null)
2969    {
2970  21 Desktop.instance.stopLoading();
2971    }
2972   
2973  21 return af;
2974    }
2975   
2976    /**
2977    * Try to reconstruct and display SplitFrame windows, where each contains
2978    * complementary dna and protein alignments. Done by pairing up AlignFrame
2979    * objects (created earlier) which have complementary viewport ids associated.
2980    */
 
2981  21 toggle protected void restoreSplitFrames()
2982    {
2983  21 List<SplitFrame> gatherTo = new ArrayList<>();
2984  21 List<AlignFrame> addedToSplitFrames = new ArrayList<>();
2985  21 Map<String, AlignFrame> dna = new HashMap<>();
2986   
2987    /*
2988    * Identify the DNA alignments
2989    */
2990  21 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
2991    .entrySet())
2992    {
2993  0 AlignFrame af = candidate.getValue();
2994  0 if (af.getViewport().getAlignment().isNucleotide())
2995    {
2996  0 dna.put(candidate.getKey().getId(), af);
2997    }
2998    }
2999   
3000    /*
3001    * Try to match up the protein complements
3002    */
3003  21 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
3004    .entrySet())
3005    {
3006  0 AlignFrame af = candidate.getValue();
3007  0 if (!af.getViewport().getAlignment().isNucleotide())
3008    {
3009  0 String complementId = candidate.getKey().getComplementId();
3010    // only non-null complements should be in the Map
3011  0 if (complementId != null && dna.containsKey(complementId))
3012    {
3013  0 final AlignFrame dnaFrame = dna.get(complementId);
3014  0 SplitFrame sf = createSplitFrame(dnaFrame, af);
3015  0 addedToSplitFrames.add(dnaFrame);
3016  0 addedToSplitFrames.add(af);
3017  0 dnaFrame.setMenusForViewport();
3018  0 af.setMenusForViewport();
3019  0 if (af.getViewport().isGatherViewsHere())
3020    {
3021  0 gatherTo.add(sf);
3022    }
3023    }
3024    }
3025    }
3026   
3027    /*
3028    * Open any that we failed to pair up (which shouldn't happen!) as
3029    * standalone AlignFrame's.
3030    */
3031  21 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
3032    .entrySet())
3033    {
3034  0 AlignFrame af = candidate.getValue();
3035  0 if (!addedToSplitFrames.contains(af))
3036    {
3037  0 Viewport view = candidate.getKey();
3038  0 Desktop.addInternalFrame(af, view.getTitle(),
3039    safeInt(view.getWidth()), safeInt(view.getHeight()));
3040  0 af.setMenusForViewport();
3041  0 System.err.println("Failed to restore view " + view.getTitle()
3042    + " to split frame");
3043    }
3044    }
3045   
3046    /*
3047    * Gather back into tabbed views as flagged.
3048    */
3049  21 for (SplitFrame sf : gatherTo)
3050    {
3051  0 Desktop.instance.gatherViews(sf);
3052    }
3053   
3054  21 splitFrameCandidates.clear();
3055    }
3056   
3057    /**
3058    * Construct and display one SplitFrame holding DNA and protein alignments.
3059    *
3060    * @param dnaFrame
3061    * @param proteinFrame
3062    * @return
3063    */
 
3064  0 toggle protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
3065    AlignFrame proteinFrame)
3066    {
3067  0 SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
3068  0 String title = MessageManager.getString("label.linked_view_title");
3069  0 int width = (int) dnaFrame.getBounds().getWidth();
3070  0 int height = (int) (dnaFrame.getBounds().getHeight()
3071    + proteinFrame.getBounds().getHeight() + 50);
3072   
3073    /*
3074    * SplitFrame location is saved to both enclosed frames
3075    */
3076  0 splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
3077  0 Desktop.addInternalFrame(splitFrame, title, width, height);
3078   
3079    /*
3080    * And compute cDNA consensus (couldn't do earlier with consensus as
3081    * mappings were not yet present)
3082    */
3083  0 proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel);
3084   
3085  0 return splitFrame;
3086    }
3087   
3088    /**
3089    * check errorMessage for a valid error message and raise an error box in the
3090    * GUI or write the current errorMessage to stderr and then clear the error
3091    * state.
3092    */
 
3093  7 toggle protected void reportErrors()
3094    {
3095  7 reportErrors(false);
3096    }
3097   
 
3098  7 toggle protected void reportErrors(final boolean saving)
3099    {
3100  7 if (errorMessage != null)
3101    {
3102  0 final String finalErrorMessage = errorMessage;
3103  0 if (raiseGUI)
3104    {
3105  0 javax.swing.SwingUtilities.invokeLater(new Runnable()
3106    {
 
3107  0 toggle @Override
3108    public void run()
3109    {
3110  0 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3111    finalErrorMessage,
3112  0 "Error " + (saving ? "saving" : "loading")
3113    + " Jalview file",
3114    JvOptionPane.WARNING_MESSAGE);
3115    }
3116    });
3117    }
3118    else
3119    {
3120  0 System.err.println("Problem loading Jalview file: " + errorMessage);
3121    }
3122    }
3123  7 errorMessage = null;
3124    }
3125   
3126    Map<String, String> alreadyLoadedPDB = new HashMap<>();
3127   
3128    /**
3129    * when set, local views will be updated from view stored in JalviewXML
3130    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
3131    * sync if this is set to true.
3132    */
3133    private final boolean updateLocalViews = false;
3134   
3135    /**
3136    * Returns the path to a temporary file holding the PDB file for the given PDB
3137    * id. The first time of asking, searches for a file of that name in the
3138    * Jalview project jar, and copies it to a new temporary file. Any repeat
3139    * requests just return the path to the file previously created.
3140    *
3141    * @param jprovider
3142    * @param pdbId
3143    * @return
3144    */
 
3145  194 toggle String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
3146    String origFile)
3147    {
3148  194 if (alreadyLoadedPDB.containsKey(pdbId))
3149    {
3150  184 return alreadyLoadedPDB.get(pdbId).toString();
3151    }
3152   
3153  10 String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
3154    origFile);
3155  10 if (tempFile != null)
3156    {
3157  10 alreadyLoadedPDB.put(pdbId, tempFile);
3158    }
3159  10 return tempFile;
3160    }
3161   
3162    /**
3163    * Copies the jar entry of given name to a new temporary file and returns the
3164    * path to the file, or null if the entry is not found.
3165    *
3166    * @param jprovider
3167    * @param jarEntryName
3168    * @param prefix
3169    * a prefix for the temporary file name, must be at least three
3170    * characters long
3171    * @param suffixModel
3172    * null or original file - so new file can be given the same suffix
3173    * as the old one
3174    * @return
3175    */
 
3176  10 toggle protected String copyJarEntry(jarInputStreamProvider jprovider,
3177    String jarEntryName, String prefix, String suffixModel)
3178    {
3179  10 String suffix = ".tmp";
3180  10 if (suffixModel == null)
3181    {
3182  0 suffixModel = jarEntryName;
3183    }
3184  10 int sfpos = suffixModel.lastIndexOf(".");
3185  10 if (sfpos > -1 && sfpos < (suffixModel.length() - 1))
3186    {
3187  10 suffix = "." + suffixModel.substring(sfpos + 1);
3188    }
3189   
3190  10 try (JarInputStream jin = jprovider.getJarInputStream())
3191    {
3192  10 JarEntry entry = null;
3193  10 do
3194    {
3195  12 entry = jin.getNextJarEntry();
3196  12 } while (entry != null && !entry.getName().equals(jarEntryName));
3197   
3198  10 if (entry != null)
3199    {
3200    // in = new BufferedReader(new InputStreamReader(jin, UTF_8));
3201  10 File outFile = File.createTempFile(prefix, suffix);
3202  10 outFile.deleteOnExit();
3203  10 try (OutputStream os = new FileOutputStream(outFile))
3204    {
3205  10 copyAll(jin, os);
3206    }
3207  10 String t = outFile.getAbsolutePath();
3208  10 return t;
3209    }
3210    else
3211    {
3212  0 warn("Couldn't find entry in Jalview Jar for " + jarEntryName);
3213    }
3214    } catch (Exception ex)
3215    {
3216  0 ex.printStackTrace();
3217    }
3218   
3219  0 return null;
3220    }
3221   
 
3222    private class JvAnnotRow
3223    {
 
3224  474 toggle public JvAnnotRow(int i, AlignmentAnnotation jaa)
3225    {
3226  474 order = i;
3227  474 template = jaa;
3228    }
3229   
3230    /**
3231    * persisted version of annotation row from which to take vis properties
3232    */
3233    public jalview.datamodel.AlignmentAnnotation template;
3234   
3235    /**
3236    * original position of the annotation row in the alignment
3237    */
3238    public int order;
3239    }
3240   
3241    /**
3242    * Load alignment frame from jalview XML DOM object
3243    *
3244    * @param jalviewModel
3245    * DOM
3246    * @param file
3247    * filename source string
3248    * @param loadTreesAndStructures
3249    * when false only create Viewport
3250    * @param jprovider
3251    * data source provider
3252    * @return alignment frame created from view stored in DOM
3253    */
 
3254  86 toggle AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
3255    boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
3256    {
3257  86 SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
3258  86 List<Sequence> vamsasSeqs = vamsasSet.getSequence();
3259   
3260    // JalviewModelSequence jms = object.getJalviewModelSequence();
3261   
3262    // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
3263    // : null;
3264  86 Viewport view = (jalviewModel.getViewport().size() > 0)
3265    ? jalviewModel.getViewport().get(0)
3266    : null;
3267   
3268    // ////////////////////////////////
3269    // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
3270    //
3271    //
3272    // If we just load in the same jar file again, the sequenceSetId
3273    // will be the same, and we end up with multiple references
3274    // to the same sequenceSet. We must modify this id on load
3275    // so that each load of the file gives a unique id
3276   
3277    /**
3278    * used to resolve correct alignment dataset for alignments with multiple
3279    * views
3280    */
3281  86 String uniqueSeqSetId = null;
3282  86 String viewId = null;
3283  86 if (view != null)
3284    {
3285  70 uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
3286  70 viewId = (view.getId() == null ? null
3287    : view.getId() + uniqueSetSuffix);
3288    }
3289   
3290    // ////////////////////////////////
3291    // LOAD SEQUENCES
3292   
3293  86 List<SequenceI> hiddenSeqs = null;
3294   
3295  86 List<SequenceI> tmpseqs = new ArrayList<>();
3296   
3297  86 boolean multipleView = false;
3298  86 SequenceI referenceseqForView = null;
3299    // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
3300  86 List<JSeq> jseqs = jalviewModel.getJSeq();
3301  86 int vi = 0; // counter in vamsasSeq array
3302  1386 for (int i = 0; i < jseqs.size(); i++)
3303    {
3304  1300 JSeq jseq = jseqs.get(i);
3305  1300 String seqId = jseq.getId();
3306   
3307  1300 SequenceI tmpSeq = seqRefIds.get(seqId);
3308  1300 if (tmpSeq != null)
3309    {
3310  947 if (!incompleteSeqs.containsKey(seqId))
3311    {
3312    // may not need this check, but keep it for at least 2.9,1 release
3313  947 if (tmpSeq.getStart() != jseq.getStart()
3314    || tmpSeq.getEnd() != jseq.getEnd())
3315    {
3316  0 System.err.println(
3317    String.format("Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
3318    tmpSeq.getName(), tmpSeq.getStart(),
3319    tmpSeq.getEnd(), jseq.getStart(),
3320    jseq.getEnd()));
3321    }
3322    }
3323    else
3324    {
3325  0 incompleteSeqs.remove(seqId);
3326    }
3327  947 if (vamsasSeqs.size() > vi
3328    && vamsasSeqs.get(vi).getId().equals(seqId))
3329    {
3330    // most likely we are reading a dataset XML document so
3331    // update from vamsasSeq section of XML for this sequence
3332  314 tmpSeq.setName(vamsasSeqs.get(vi).getName());
3333  314 tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription());
3334  314 tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence());
3335  314 vi++;
3336    }
3337    else
3338    {
3339    // reading multiple views, so vamsasSeq set is a subset of JSeq
3340  633 multipleView = true;
3341    }
3342  947 tmpSeq.setStart(jseq.getStart());
3343  947 tmpSeq.setEnd(jseq.getEnd());
3344  947 tmpseqs.add(tmpSeq);
3345    }
3346    else
3347    {
3348  353 Sequence vamsasSeq = vamsasSeqs.get(vi);
3349  353 tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(),
3350    vamsasSeq.getSequence());
3351  353 tmpSeq.setDescription(vamsasSeq.getDescription());
3352  353 tmpSeq.setStart(jseq.getStart());
3353  353 tmpSeq.setEnd(jseq.getEnd());
3354  353 tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
3355  353 seqRefIds.put(vamsasSeq.getId(), tmpSeq);
3356  353 tmpseqs.add(tmpSeq);
3357  353 vi++;
3358    }
3359   
3360  1300 if (safeBoolean(jseq.isViewreference()))
3361    {
3362  5 referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
3363    }
3364   
3365  1300 if (jseq.isHidden() != null && jseq.isHidden().booleanValue())
3366    {
3367  186 if (hiddenSeqs == null)
3368    {
3369  42 hiddenSeqs = new ArrayList<>();
3370    }
3371   
3372  186 hiddenSeqs.add(tmpSeq);
3373    }
3374    }
3375   
3376    // /
3377    // Create the alignment object from the sequence set
3378    // ///////////////////////////////
3379  86 SequenceI[] orderedSeqs = tmpseqs
3380    .toArray(new SequenceI[tmpseqs.size()]);
3381   
3382  86 AlignmentI al = null;
3383    // so we must create or recover the dataset alignment before going further
3384    // ///////////////////////////////
3385  86 if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
3386    {
3387    // older jalview projects do not have a dataset - so creat alignment and
3388    // dataset
3389  30 al = new Alignment(orderedSeqs);
3390  30 al.setDataset(null);
3391    }
3392    else
3393    {
3394  56 boolean isdsal = jalviewModel.getViewport().isEmpty();
3395  56 if (isdsal)
3396    {
3397    // we are importing a dataset record, so
3398    // recover reference to an alignment already materialsed as dataset
3399  16 al = getDatasetFor(vamsasSet.getDatasetId());
3400    }
3401  56 if (al == null)
3402    {
3403    // materialse the alignment
3404  40 al = new Alignment(orderedSeqs);
3405    }
3406  56 if (isdsal)
3407    {
3408  16 addDatasetRef(vamsasSet.getDatasetId(), al);
3409    }
3410   
3411    // finally, verify all data in vamsasSet is actually present in al
3412    // passing on flag indicating if it is actually a stored dataset
3413  56 recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
3414    }
3415   
3416  86 if (referenceseqForView != null)
3417    {
3418  5 al.setSeqrep(referenceseqForView);
3419    }
3420    // / Add the alignment properties
3421  107 for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++)
3422    {
3423  21 SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties()
3424    .get(i);
3425  21 al.setProperty(ssp.getKey(), ssp.getValue());
3426    }
3427   
3428    // ///////////////////////////////
3429   
3430  86 Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
3431  86 if (!multipleView)
3432    {
3433    // load sequence features, database references and any associated PDB
3434    // structures for the alignment
3435    //
3436    // prior to 2.10, this part would only be executed the first time a
3437    // sequence was encountered, but not afterwards.
3438    // now, for 2.10 projects, this is also done if the xml doc includes
3439    // dataset sequences not actually present in any particular view.
3440    //
3441  712 for (int i = 0; i < vamsasSeqs.size(); i++)
3442    {
3443  667 JSeq jseq = jseqs.get(i);
3444  667 if (jseq.getFeatures().size() > 0)
3445    {
3446  231 List<Feature> features = jseq.getFeatures();
3447  136150 for (int f = 0; f < features.size(); f++)
3448    {
3449  135919 Feature feat = features.get(f);
3450  135919 SequenceFeature sf = new SequenceFeature(feat.getType(),
3451    feat.getDescription(), feat.getBegin(), feat.getEnd(),
3452    safeFloat(feat.getScore()), feat.getFeatureGroup());
3453  135919 sf.setStatus(feat.getStatus());
3454   
3455    /*
3456    * load any feature attributes - include map-valued attributes
3457    */
3458  135919 Map<String, Map<String, String>> mapAttributes = new HashMap<>();
3459  653563 for (int od = 0; od < feat.getOtherData().size(); od++)
3460    {
3461  517644 OtherData keyValue = feat.getOtherData().get(od);
3462  517644 String attributeName = keyValue.getKey();
3463  517644 String attributeValue = keyValue.getValue();
3464  517644 if (attributeName.startsWith("LINK"))
3465    {
3466  6725 sf.addLink(attributeValue);
3467    }
3468    else
3469    {
3470  510919 String subAttribute = keyValue.getKey2();
3471  510919 if (subAttribute == null)
3472    {
3473    // simple string-valued attribute
3474  510899 sf.setValue(attributeName, attributeValue);
3475    }
3476    else
3477    {
3478    // attribute 'key' has sub-attribute 'key2'
3479  20 if (!mapAttributes.containsKey(attributeName))
3480    {
3481  20 mapAttributes.put(attributeName, new HashMap<>());
3482    }
3483  20 mapAttributes.get(attributeName).put(subAttribute,
3484    attributeValue);
3485    }
3486    }
3487    }
3488  135919 for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
3489    .entrySet())
3490    {
3491  20 sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
3492    }
3493   
3494    // adds feature to datasequence's feature set (since Jalview 2.10)
3495  135919 al.getSequenceAt(i).addSequenceFeature(sf);
3496    }
3497    }
3498  667 if (vamsasSeqs.get(i).getDBRef().size() > 0)
3499    {
3500    // adds dbrefs to datasequence's set (since Jalview 2.10)
3501  222 addDBRefs(
3502  222 al.getSequenceAt(i).getDatasetSequence() == null
3503    ? al.getSequenceAt(i)
3504    : al.getSequenceAt(i).getDatasetSequence(),
3505    vamsasSeqs.get(i));
3506    }
3507  667 if (jseq.getPdbids().size() > 0)
3508    {
3509  47 List<Pdbids> ids = jseq.getPdbids();
3510  301 for (int p = 0; p < ids.size(); p++)
3511    {
3512  254 Pdbids pdbid = ids.get(p);
3513  254 jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
3514  254 entry.setId(pdbid.getId());
3515  254 if (pdbid.getType() != null)
3516    {
3517  38 if (PDBEntry.Type.getType(pdbid.getType()) != null)
3518    {
3519  38 entry.setType(PDBEntry.Type.getType(pdbid.getType()));
3520    }
3521    else
3522    {
3523  0 entry.setType(PDBEntry.Type.FILE);
3524    }
3525    }
3526    // jprovider is null when executing 'New View'
3527  254 if (pdbid.getFile() != null && jprovider != null)
3528    {
3529  34 if (!pdbloaded.containsKey(pdbid.getFile()))
3530    {
3531  34 entry.setFile(loadPDBFile(jprovider, pdbid.getId(),
3532    pdbid.getFile()));
3533    }
3534    else
3535    {
3536  0 entry.setFile(pdbloaded.get(pdbid.getId()).toString());
3537    }
3538    }
3539    /*
3540    if (pdbid.getPdbentryItem() != null)
3541    {
3542    for (PdbentryItem item : pdbid.getPdbentryItem())
3543    {
3544    for (Property pr : item.getProperty())
3545    {
3546    entry.setProperty(pr.getName(), pr.getValue());
3547    }
3548    }
3549    }
3550    */
3551  254 for (Property prop : pdbid.getProperty())
3552    {
3553  22 entry.setProperty(prop.getName(), prop.getValue());
3554    }
3555  254 StructureSelectionManager
3556    .getStructureSelectionManager(Desktop.instance)
3557    .registerPDBEntry(entry);
3558    // adds PDBEntry to datasequence's set (since Jalview 2.10)
3559  254 if (al.getSequenceAt(i).getDatasetSequence() != null)
3560    {
3561  101 al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
3562    }
3563    else
3564    {
3565  153 al.getSequenceAt(i).addPDBId(entry);
3566    }
3567    }
3568    }
3569    }
3570    } // end !multipleview
3571   
3572    // ///////////////////////////////
3573    // LOAD SEQUENCE MAPPINGS
3574   
3575  86 if (vamsasSet.getAlcodonFrame().size() > 0)
3576    {
3577    // TODO Potentially this should only be done once for all views of an
3578    // alignment
3579  0 List<AlcodonFrame> alc = vamsasSet.getAlcodonFrame();
3580  0 for (int i = 0; i < alc.size(); i++)
3581    {
3582  0 AlignedCodonFrame cf = new AlignedCodonFrame();
3583  0 if (alc.get(i).getAlcodMap().size() > 0)
3584    {
3585  0 List<AlcodMap> maps = alc.get(i).getAlcodMap();
3586  0 for (int m = 0; m < maps.size(); m++)
3587    {
3588  0 AlcodMap map = maps.get(m);
3589  0 SequenceI dnaseq = seqRefIds.get(map.getDnasq());
3590    // Load Mapping
3591  0 jalview.datamodel.Mapping mapping = null;
3592    // attach to dna sequence reference.
3593  0 if (map.getMapping() != null)
3594    {
3595  0 mapping = addMapping(map.getMapping());
3596  0 if (dnaseq != null && mapping.getTo() != null)
3597    {
3598  0 cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
3599    }
3600    else
3601    {
3602    // defer to later
3603  0 frefedSequence.add(
3604    newAlcodMapRef(map.getDnasq(), cf, mapping));
3605    }
3606    }
3607    }
3608  0 al.addCodonFrame(cf);
3609    }
3610    }
3611    }
3612   
3613    // ////////////////////////////////
3614    // LOAD ANNOTATIONS
3615  86 List<JvAnnotRow> autoAlan = new ArrayList<>();
3616   
3617    /*
3618    * store any annotations which forward reference a group's ID
3619    */
3620  86 Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<>();
3621   
3622  86 if (vamsasSet.getAnnotation().size()/*Count()*/ > 0)
3623    {
3624  75 List<Annotation> an = vamsasSet.getAnnotation();
3625   
3626  591 for (int i = 0; i < an.size(); i++)
3627    {
3628  516 Annotation annotation = an.get(i);
3629   
3630    /**
3631    * test if annotation is automatically calculated for this view only
3632    */
3633  516 boolean autoForView = false;
3634  516 if (annotation.getLabel().equals("Quality")
3635    || annotation.getLabel().equals("Conservation")
3636    || annotation.getLabel().equals("Consensus"))
3637    {
3638    // Kludge for pre 2.5 projects which lacked the autocalculated flag
3639  184 autoForView = true;
3640    // JAXB has no has() test; schema defaults value to false
3641    // if (!annotation.hasAutoCalculated())
3642    // {
3643    // annotation.setAutoCalculated(true);
3644    // }
3645    }
3646  516 if (autoForView || annotation.isAutoCalculated())
3647    {
3648    // remove ID - we don't recover annotation from other views for
3649    // view-specific annotation
3650  238 annotation.setId(null);
3651    }
3652   
3653    // set visibility for other annotation in this view
3654  516 String annotationId = annotation.getId();
3655  516 if (annotationId != null && annotationIds.containsKey(annotationId))
3656    {
3657  0 AlignmentAnnotation jda = annotationIds.get(annotationId);
3658    // in principle Visible should always be true for annotation displayed
3659    // in multiple views
3660  0 if (annotation.isVisible() != null)
3661    {
3662  0 jda.visible = annotation.isVisible();
3663    }
3664   
3665  0 al.addAnnotation(jda);
3666   
3667  0 continue;
3668    }
3669    // Construct new annotation from model.
3670  516 List<AnnotationElement> ae = annotation.getAnnotationElement();
3671  516 jalview.datamodel.Annotation[] anot = null;
3672  516 java.awt.Color firstColour = null;
3673  516 int anpos;
3674  516 if (!annotation.isScoreOnly())
3675    {
3676  516 anot = new jalview.datamodel.Annotation[al.getWidth()];
3677  266231 for (int aa = 0; aa < ae.size() && aa < anot.length; aa++)
3678    {
3679  265715 AnnotationElement annElement = ae.get(aa);
3680  265715 anpos = annElement.getPosition();
3681   
3682  265715 if (anpos >= anot.length)
3683    {
3684  0 continue;
3685    }
3686   
3687  265715 float value = safeFloat(annElement.getValue());
3688  265715 anot[anpos] = new jalview.datamodel.Annotation(
3689    annElement.getDisplayCharacter(),
3690    annElement.getDescription(),
3691  265715 (annElement.getSecondaryStructure() == null
3692    || annElement.getSecondaryStructure()
3693    .length() == 0)
3694    ? ' '
3695    : annElement
3696    .getSecondaryStructure()
3697    .charAt(0),
3698    value);
3699  265715 anot[anpos].colour = new Color(safeInt(annElement.getColour()));
3700  265715 if (firstColour == null)
3701    {
3702  516 firstColour = anot[anpos].colour;
3703    }
3704    }
3705    }
3706  516 jalview.datamodel.AlignmentAnnotation jaa = null;
3707   
3708  516 if (annotation.isGraph())
3709    {
3710  341 float llim = 0, hlim = 0;
3711    // if (autoForView || an[i].isAutoCalculated()) {
3712    // hlim=11f;
3713    // }
3714  341 jaa = new jalview.datamodel.AlignmentAnnotation(
3715    annotation.getLabel(), annotation.getDescription(), anot,
3716    llim, hlim, safeInt(annotation.getGraphType()));
3717   
3718  341 jaa.graphGroup = safeInt(annotation.getGraphGroup());
3719  341 jaa._linecolour = firstColour;
3720  341 if (annotation.getThresholdLine() != null)
3721    {
3722  30 jaa.setThreshold(new jalview.datamodel.GraphLine(
3723    safeFloat(annotation.getThresholdLine().getValue()),
3724    annotation.getThresholdLine().getLabel(),
3725    new java.awt.Color(safeInt(
3726    annotation.getThresholdLine().getColour()))));
3727    }
3728  341 if (autoForView || annotation.isAutoCalculated())
3729    {
3730    // Hardwire the symbol display line to ensure that labels for
3731    // histograms are displayed
3732  238 jaa.hasText = true;
3733    }
3734    }
3735    else
3736    {
3737  175 jaa = new jalview.datamodel.AlignmentAnnotation(
3738    annotation.getLabel(), annotation.getDescription(), anot);
3739  175 jaa._linecolour = firstColour;
3740    }
3741    // register new annotation
3742  516 if (annotation.getId() != null)
3743    {
3744  278 annotationIds.put(annotation.getId(), jaa);
3745  278 jaa.annotationId = annotation.getId();
3746    }
3747    // recover sequence association
3748  516 String sequenceRef = annotation.getSequenceRef();
3749  516 if (sequenceRef != null)
3750    {
3751    // from 2.9 sequenceRef is to sequence id (JAL-1781)
3752  203 SequenceI sequence = seqRefIds.get(sequenceRef);
3753  203 if (sequence == null)
3754    {
3755    // in pre-2.9 projects sequence ref is to sequence name
3756  0 sequence = al.findName(sequenceRef);
3757    }
3758  203 if (sequence != null)
3759    {
3760  203 jaa.createSequenceMapping(sequence, 1, true);
3761  203 sequence.addAlignmentAnnotation(jaa);
3762    }
3763    }
3764    // and make a note of any group association
3765  516 if (annotation.getGroupRef() != null
3766    && annotation.getGroupRef().length() > 0)
3767    {
3768  24 List<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
3769    .get(annotation.getGroupRef());
3770  24 if (aal == null)
3771    {
3772  24 aal = new ArrayList<>();
3773  24 groupAnnotRefs.put(annotation.getGroupRef(), aal);
3774    }
3775  24 aal.add(jaa);
3776    }
3777   
3778  516 if (annotation.getScore() != null)
3779    {
3780  139 jaa.setScore(annotation.getScore().doubleValue());
3781    }
3782  516 if (annotation.isVisible() != null)
3783    {
3784  516 jaa.visible = annotation.isVisible().booleanValue();
3785    }
3786   
3787  516 if (annotation.isCentreColLabels() != null)
3788    {
3789  516 jaa.centreColLabels = annotation.isCentreColLabels()
3790    .booleanValue();
3791    }
3792   
3793  516 if (annotation.isScaleColLabels() != null)
3794    {
3795  516 jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue();
3796    }
3797  516 if (annotation.isAutoCalculated())
3798    {
3799    // newer files have an 'autoCalculated' flag and store calculation
3800    // state in viewport properties
3801  238 jaa.autoCalculated = true; // means annotation will be marked for
3802    // update at end of load.
3803    }
3804  516 if (annotation.getGraphHeight() != null)
3805    {
3806  516 jaa.graphHeight = annotation.getGraphHeight().intValue();
3807    }
3808  516 jaa.belowAlignment = annotation.isBelowAlignment();
3809  516 jaa.setCalcId(annotation.getCalcId());
3810  516 if (annotation.getProperty().size() > 0)
3811    {
3812  0 for (Annotation.Property prop : annotation
3813    .getProperty())
3814    {
3815  0 jaa.setProperty(prop.getName(), prop.getValue());
3816    }
3817    }
3818  516 if (jaa.autoCalculated)
3819    {
3820  238 autoAlan.add(new JvAnnotRow(i, jaa));
3821    }
3822    else
3823    // if (!autoForView)
3824    {
3825    // add autocalculated group annotation and any user created annotation
3826    // for the view
3827  278 al.addAnnotation(jaa);
3828    }
3829    }
3830    }
3831    // ///////////////////////
3832    // LOAD GROUPS
3833    // Create alignment markup and styles for this view
3834  86 if (jalviewModel.getJGroup().size() > 0)
3835    {
3836  27 List<JGroup> groups = jalviewModel.getJGroup();
3837  27 boolean addAnnotSchemeGroup = false;
3838  70 for (int i = 0; i < groups.size(); i++)
3839    {
3840  43 JGroup jGroup = groups.get(i);
3841  43 ColourSchemeI cs = null;
3842  43 if (jGroup.getColour() != null)
3843    {
3844  19 if (jGroup.getColour().startsWith("ucs"))
3845    {
3846  0 cs = getUserColourScheme(jalviewModel, jGroup.getColour());
3847    }
3848  19 else if (jGroup.getColour().equals("AnnotationColourGradient")
3849    && jGroup.getAnnotationColours() != null)
3850    {
3851  1 addAnnotSchemeGroup = true;
3852    }
3853    else
3854    {
3855  18 cs = ColourSchemeProperty.getColourScheme(null, al,
3856    jGroup.getColour());
3857    }
3858    }
3859  43 int pidThreshold = safeInt(jGroup.getPidThreshold());
3860   
3861  43 Vector<SequenceI> seqs = new Vector<>();
3862   
3863  367 for (int s = 0; s < jGroup.getSeq().size(); s++)
3864    {
3865  324 String seqId = jGroup.getSeq().get(s);
3866  324 SequenceI ts = seqRefIds.get(seqId);
3867   
3868  324 if (ts != null)
3869    {
3870  324 seqs.addElement(ts);
3871    }
3872    }
3873   
3874  43 if (seqs.size() < 1)
3875    {
3876  0 continue;
3877    }
3878   
3879  43 SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
3880    safeBoolean(jGroup.isDisplayBoxes()),
3881    safeBoolean(jGroup.isDisplayText()),
3882    safeBoolean(jGroup.isColourText()),
3883    safeInt(jGroup.getStart()), safeInt(jGroup.getEnd()));
3884  43 sg.getGroupColourScheme().setThreshold(pidThreshold, true);
3885  43 sg.getGroupColourScheme()
3886    .setConservationInc(safeInt(jGroup.getConsThreshold()));
3887  43 sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour())));
3888   
3889  43 sg.textColour = new Color(safeInt(jGroup.getTextCol1()));
3890  43 sg.textColour2 = new Color(safeInt(jGroup.getTextCol2()));
3891  43 sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved()));
3892  43 sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold());
3893    // attributes with a default in the schema are never null
3894  43 sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
3895  43 sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
3896  43 sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
3897  43 sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus());
3898  43 if (jGroup.getConsThreshold() != null
3899    && jGroup.getConsThreshold().intValue() != 0)
3900    {
3901  2 Conservation c = new Conservation("All", sg.getSequences(null), 0,
3902    sg.getWidth() - 1);
3903  2 c.calculate();
3904  2 c.verdict(false, 25);
3905  2 sg.cs.setConservation(c);
3906    }
3907   
3908  43 if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
3909    {
3910    // re-instate unique group/annotation row reference
3911  24 List<AlignmentAnnotation> jaal = groupAnnotRefs
3912    .get(jGroup.getId());
3913  24 if (jaal != null)
3914    {
3915  24 for (AlignmentAnnotation jaa : jaal)
3916    {
3917  24 jaa.groupRef = sg;
3918  24 if (jaa.autoCalculated)
3919    {
3920    // match up and try to set group autocalc alignment row for this
3921    // annotation
3922  24 if (jaa.label.startsWith("Consensus for "))
3923    {
3924  24 sg.setConsensus(jaa);
3925    }
3926    // match up and try to set group autocalc alignment row for this
3927    // annotation
3928  24 if (jaa.label.startsWith("Conservation for "))
3929    {
3930  0 sg.setConservationRow(jaa);
3931    }
3932    }
3933    }
3934    }
3935    }
3936  43 al.addGroup(sg);
3937  43 if (addAnnotSchemeGroup)
3938    {
3939    // reconstruct the annotation colourscheme
3940  1 sg.setColourScheme(constructAnnotationColour(
3941    jGroup.getAnnotationColours(), null, al, jalviewModel, false));
3942    }
3943    }
3944    }
3945  86 if (view == null)
3946    {
3947    // only dataset in this model, so just return.
3948  16 return null;
3949    }
3950    // ///////////////////////////////
3951    // LOAD VIEWPORT
3952   
3953  70 AlignFrame af = null;
3954  70 AlignViewport av = null;
3955    // now check to see if we really need to create a new viewport.
3956  70 if (multipleView && viewportsAdded.size() == 0)
3957    {
3958    // We recovered an alignment for which a viewport already exists.
3959    // TODO: fix up any settings necessary for overlaying stored state onto
3960    // state recovered from another document. (may not be necessary).
3961    // we may need a binding from a viewport in memory to one recovered from
3962    // XML.
3963    // and then recover its containing af to allow the settings to be applied.
3964    // TODO: fix for vamsas demo
3965  0 System.err.println(
3966    "About to recover a viewport for existing alignment: Sequence set ID is "
3967    + uniqueSeqSetId);
3968  0 Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
3969  0 if (seqsetobj != null)
3970    {
3971  0 if (seqsetobj instanceof String)
3972    {
3973  0 uniqueSeqSetId = (String) seqsetobj;
3974  0 System.err.println(
3975    "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
3976    + uniqueSeqSetId);
3977    }
3978    else
3979    {
3980  0 System.err.println(
3981    "Warning : Collision between sequence set ID string and existing jalview object mapping.");
3982    }
3983   
3984    }
3985    }
3986    /**
3987    * indicate that annotation colours are applied across all groups (pre
3988    * Jalview 2.8.1 behaviour)
3989    */
3990  70 boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
3991    jalviewModel.getVersion());
3992   
3993  70 AlignmentPanel ap = null;
3994  70 boolean isnewview = true;
3995  70 if (viewId != null)
3996    {
3997    // Check to see if this alignment already has a view id == viewId
3998  64 jalview.gui.AlignmentPanel views[] = Desktop
3999    .getAlignmentPanels(uniqueSeqSetId);
4000  64 if (views != null && views.length > 0)
4001    {
4002  135 for (int v = 0; v < views.length; v++)
4003    {
4004  94 if (views[v].av.getViewId().equalsIgnoreCase(viewId))
4005    {
4006    // recover the existing alignpanel, alignframe, viewport
4007  0 af = views[v].alignFrame;
4008  0 av = views[v].av;
4009  0 ap = views[v];
4010    // TODO: could even skip resetting view settings if we don't want to
4011    // change the local settings from other jalview processes
4012  0 isnewview = false;
4013    }
4014    }
4015    }
4016    }
4017   
4018  70 if (isnewview)
4019    {
4020  70 af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
4021    uniqueSeqSetId, viewId, autoAlan);
4022  70 av = af.getViewport();
4023  70 ap = af.alignPanel;
4024    }
4025   
4026    /*
4027    * Load any trees, PDB structures and viewers
4028    *
4029    * Not done if flag is false (when this method is used for New View)
4030    */
4031  70 if (loadTreesAndStructures)
4032    {
4033  64 loadTrees(jalviewModel, view, af, av, ap);
4034  64 loadPCAViewers(jalviewModel, ap);
4035  64 loadPDBStructures(jprovider, jseqs, af, ap);
4036  64 loadRnaViewers(jprovider, jseqs, ap);
4037    }
4038    // and finally return.
4039  70 return af;
4040    }
4041   
4042    /**
4043    * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna
4044    * panel is restored from separate jar entries, two (gapped and trimmed) per
4045    * sequence and secondary structure.
4046    *
4047    * Currently each viewer shows just one sequence and structure (gapped and
4048    * trimmed), however this method is designed to support multiple sequences or
4049    * structures in viewers if wanted in future.
4050    *
4051    * @param jprovider
4052    * @param jseqs
4053    * @param ap
4054    */
 
4055  64 toggle private void loadRnaViewers(jarInputStreamProvider jprovider,
4056    List<JSeq> jseqs, AlignmentPanel ap)
4057    {
4058    /*
4059    * scan the sequences for references to viewers; create each one the first
4060    * time it is referenced, add Rna models to existing viewers
4061    */
4062  64 for (JSeq jseq : jseqs)
4063    {
4064  975 for (int i = 0; i < jseq.getRnaViewer().size(); i++)
4065    {
4066  0 RnaViewer viewer = jseq.getRnaViewer().get(i);
4067  0 AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
4068    ap);
4069   
4070  0 for (int j = 0; j < viewer.getSecondaryStructure().size(); j++)
4071    {
4072  0 SecondaryStructure ss = viewer.getSecondaryStructure().get(j);
4073  0 SequenceI seq = seqRefIds.get(jseq.getId());
4074  0 AlignmentAnnotation ann = this.annotationIds
4075    .get(ss.getAnnotationId());
4076   
4077    /*
4078    * add the structure to the Varna display (with session state copied
4079    * from the jar to a temporary file)
4080    */
4081  0 boolean gapped = safeBoolean(ss.isGapped());
4082  0 String rnaTitle = ss.getTitle();
4083  0 String sessionState = ss.getViewerState();
4084  0 String tempStateFile = copyJarEntry(jprovider, sessionState,
4085    "varna", null);
4086  0 RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
4087  0 appVarna.addModelSession(rna, rnaTitle, tempStateFile);
4088    }
4089  0 appVarna.setInitialSelection(safeInt(viewer.getSelectedRna()));
4090    }
4091    }
4092    }
4093   
4094    /**
4095    * Locate and return an already instantiated matching AppVarna, or create one
4096    * if not found
4097    *
4098    * @param viewer
4099    * @param viewIdSuffix
4100    * @param ap
4101    * @return
4102    */
 
4103  0 toggle protected AppVarna findOrCreateVarnaViewer(RnaViewer viewer,
4104    String viewIdSuffix, AlignmentPanel ap)
4105    {
4106    /*
4107    * on each load a suffix is appended to the saved viewId, to avoid conflicts
4108    * if load is repeated
4109    */
4110  0 String postLoadId = viewer.getViewId() + viewIdSuffix;
4111  0 for (JInternalFrame frame : getAllFrames())
4112    {
4113  0 if (frame instanceof AppVarna)
4114    {
4115  0 AppVarna varna = (AppVarna) frame;
4116  0 if (postLoadId.equals(varna.getViewId()))
4117    {
4118    // this viewer is already instantiated
4119    // could in future here add ap as another 'parent' of the
4120    // AppVarna window; currently just 1-to-many
4121  0 return varna;
4122    }
4123    }
4124    }
4125   
4126    /*
4127    * viewer not found - make it
4128    */
4129  0 RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(),
4130    safeInt(viewer.getXpos()), safeInt(viewer.getYpos()),
4131    safeInt(viewer.getWidth()), safeInt(viewer.getHeight()),
4132    safeInt(viewer.getDividerLocation()));
4133  0 AppVarna varna = new AppVarna(model, ap);
4134   
4135  0 return varna;
4136    }
4137   
4138    /**
4139    * Load any saved trees
4140    *
4141    * @param jm
4142    * @param view
4143    * @param af
4144    * @param av
4145    * @param ap
4146    */
 
4147  64 toggle protected void loadTrees(JalviewModel jm, Viewport view,
4148    AlignFrame af, AlignViewport av, AlignmentPanel ap)
4149    {
4150    // TODO result of automated refactoring - are all these parameters needed?
4151  64 try
4152    {
4153  72 for (int t = 0; t < jm.getTree().size(); t++)
4154    {
4155   
4156  8 Tree tree = jm.getTree().get(t);
4157   
4158  8 TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
4159  8 if (tp == null)
4160    {
4161  8 tp = af.showNewickTree(new NewickFile(tree.getNewick()),
4162    tree.getTitle(), safeInt(tree.getWidth()),
4163    safeInt(tree.getHeight()), safeInt(tree.getXpos()),
4164    safeInt(tree.getYpos()));
4165  8 if (tree.getId() != null)
4166    {
4167    // perhaps bind the tree id to something ?
4168    }
4169    }
4170    else
4171    {
4172    // update local tree attributes ?
4173    // TODO: should check if tp has been manipulated by user - if so its
4174    // settings shouldn't be modified
4175  0 tp.setTitle(tree.getTitle());
4176  0 tp.setBounds(new Rectangle(safeInt(tree.getXpos()),
4177    safeInt(tree.getYpos()), safeInt(tree.getWidth()),
4178    safeInt(tree.getHeight())));
4179  0 tp.setViewport(av); // af.viewport;
4180    // TODO: verify 'associate with all views' works still
4181  0 tp.getTreeCanvas().setViewport(av); // af.viewport;
4182  0 tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
4183    }
4184  8 tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
4185  8 if (tp == null)
4186    {
4187  0 warn("There was a problem recovering stored Newick tree: \n"
4188    + tree.getNewick());
4189  0 continue;
4190    }
4191   
4192  8 tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow()));
4193  8 tp.fitToWindow_actionPerformed(null);
4194   
4195  8 if (tree.getFontName() != null)
4196    {
4197  8 tp.setTreeFont(
4198    new Font(tree.getFontName(), safeInt(tree.getFontStyle()),
4199    safeInt(tree.getFontSize())));
4200    }
4201    else
4202    {
4203  0 tp.setTreeFont(
4204    new Font(view.getFontName(), safeInt(view.getFontStyle()),
4205    safeInt(view.getFontSize())));
4206    }
4207   
4208  8 tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked()));
4209  8 tp.showBootstrap(safeBoolean(tree.isShowBootstrap()));
4210  8 tp.showDistances(safeBoolean(tree.isShowDistances()));
4211   
4212  8 tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold()));
4213   
4214  8 if (safeBoolean(tree.isCurrentTree()))
4215    {
4216  8 af.getViewport().setCurrentTree(tp.getTree());
4217    }
4218    }
4219   
4220    } catch (Exception ex)
4221    {
4222  0 ex.printStackTrace();
4223    }
4224    }
4225   
4226    /**
4227    * Load and link any saved structure viewers.
4228    *
4229    * @param jprovider
4230    * @param jseqs
4231    * @param af
4232    * @param ap
4233    */
 
4234  64 toggle protected void loadPDBStructures(jarInputStreamProvider jprovider,
4235    List<JSeq> jseqs, AlignFrame af, AlignmentPanel ap)
4236    {
4237    /*
4238    * Run through all PDB ids on the alignment, and collect mappings between
4239    * distinct view ids and all sequences referring to that view.
4240    */
4241  64 Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<>();
4242   
4243  1039 for (int i = 0; i < jseqs.size(); i++)
4244    {
4245  975 JSeq jseq = jseqs.get(i);
4246  975 if (jseq.getPdbids().size() > 0)
4247    {
4248  128 List<Pdbids> ids = jseq.getPdbids();
4249  325 for (int p = 0; p < ids.size(); p++)
4250    {
4251  197 Pdbids pdbid = ids.get(p);
4252  197 final int structureStateCount = pdbid.getStructureState().size();
4253  277 for (int s = 0; s < structureStateCount; s++)
4254    {
4255    // check to see if we haven't already created this structure view
4256  80 final StructureState structureState = pdbid
4257    .getStructureState().get(s);
4258  80 String sviewid = (structureState.getViewId() == null) ? null
4259    : structureState.getViewId() + uniqueSetSuffix;
4260  80 jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
4261    // Originally : pdbid.getFile()
4262    // : TODO: verify external PDB file recovery still works in normal
4263    // jalview project load
4264  80 jpdb.setFile(
4265    loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile()));
4266  80 jpdb.setId(pdbid.getId());
4267   
4268  80 int x = safeInt(structureState.getXpos());
4269  80 int y = safeInt(structureState.getYpos());
4270  80 int width = safeInt(structureState.getWidth());
4271  80 int height = safeInt(structureState.getHeight());
4272   
4273    // Probably don't need to do this anymore...
4274    // Desktop.desktop.getComponentAt(x, y);
4275    // TODO: NOW: check that this recovers the PDB file correctly.
4276  80 String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
4277    pdbid.getFile());
4278  80 jalview.datamodel.SequenceI seq = seqRefIds
4279    .get(jseq.getId() + "");
4280  80 if (sviewid == null)
4281    {
4282  0 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + ","
4283    + height;
4284    }
4285  80 if (!structureViewers.containsKey(sviewid))
4286    {
4287  40 String viewerType = structureState.getType();
4288  40 if (viewerType == null) // pre Jalview 2.9
4289    {
4290  30 viewerType = ViewerType.JMOL.toString();
4291    }
4292  40 structureViewers.put(sviewid,
4293    new StructureViewerModel(x, y, width, height, false,
4294    false, true, structureState.getViewId(),
4295    viewerType));
4296    // Legacy pre-2.7 conversion JAL-823 :
4297    // do not assume any view has to be linked for colour by
4298    // sequence
4299    }
4300   
4301    // assemble String[] { pdb files }, String[] { id for each
4302    // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
4303    // seqs_file 2}, boolean[] {
4304    // linkAlignPanel,superposeWithAlignpanel}} from hash
4305  80 StructureViewerModel jmoldat = structureViewers.get(sviewid);
4306  80 jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
4307    || structureState.isAlignwithAlignPanel());
4308   
4309    /*
4310    * Default colour by linked panel to false if not specified (e.g.
4311    * for pre-2.7 projects)
4312    */
4313  80 boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
4314  80 colourWithAlignPanel |= structureState.isColourwithAlignPanel();
4315  80 jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
4316   
4317    /*
4318    * Default colour by viewer to true if not specified (e.g. for
4319    * pre-2.7 projects)
4320    */
4321  80 boolean colourByViewer = jmoldat.isColourByViewer();
4322  80 colourByViewer &= structureState.isColourByJmol();
4323  80 jmoldat.setColourByViewer(colourByViewer);
4324   
4325  80 if (jmoldat.getStateData().length() < structureState
4326    .getValue()/*Content()*/.length())
4327    {
4328  30 jmoldat.setStateData(structureState.getValue());// Content());
4329    }
4330  80 if (pdbid.getFile() != null)
4331    {
4332  80 File mapkey = new File(pdbid.getFile());
4333  80 StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
4334  80 if (seqstrmaps == null)
4335    {
4336  40 jmoldat.getFileData().put(mapkey,
4337    seqstrmaps = jmoldat.new StructureData(pdbFile,
4338    pdbid.getId()));
4339    }
4340  80 if (!seqstrmaps.getSeqList().contains(seq))
4341    {
4342  80 seqstrmaps.getSeqList().add(seq);
4343    // TODO and chains?
4344    }
4345    }
4346    else
4347    {
4348  0 errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
4349  0 warn(errorMessage);
4350    }
4351    }
4352    }
4353    }
4354    }
4355    // Instantiate the associated structure views
4356  64 for (Entry<String, StructureViewerModel> entry : structureViewers
4357    .entrySet())
4358    {
4359  40 try
4360    {
4361  40 createOrLinkStructureViewer(entry, af, ap, jprovider);
4362    } catch (Exception e)
4363    {
4364  0 System.err.println(
4365    "Error loading structure viewer: " + e.getMessage());
4366    // failed - try the next one
4367    }
4368    }
4369    }
4370   
4371    /**
4372    *
4373    * @param viewerData
4374    * @param af
4375    * @param ap
4376    * @param jprovider
4377    */
 
4378  40 toggle protected void createOrLinkStructureViewer(
4379    Entry<String, StructureViewerModel> viewerData, AlignFrame af,
4380    AlignmentPanel ap, jarInputStreamProvider jprovider)
4381    {
4382  40 final StructureViewerModel stateData = viewerData.getValue();
4383   
4384    /*
4385    * Search for any viewer windows already open from other alignment views
4386    * that exactly match the stored structure state
4387    */
4388  40 StructureViewerBase comp = findMatchingViewer(viewerData);
4389   
4390  40 if (comp != null)
4391    {
4392  35 linkStructureViewer(ap, comp, stateData);
4393  35 return;
4394    }
4395   
4396  5 String type = stateData.getType();
4397  5 try
4398    {
4399  5 ViewerType viewerType = ViewerType.valueOf(type);
4400  5 createStructureViewer(viewerType, viewerData, af, jprovider);
4401    } catch (IllegalArgumentException | NullPointerException e)
4402    {
4403    // TODO JAL-3619 show error dialog / offer an alternative viewer
4404  0 Cache.log.error(
4405    "Invalid structure viewer type: " + type);
4406    }
4407    }
4408   
4409    /**
4410    * Generates a name for the entry in the project jar file to hold state
4411    * information for a structure viewer
4412    *
4413    * @param viewId
4414    * @return
4415    */
 
4416  4 toggle protected String getViewerJarEntryName(String viewId)
4417    {
4418  4 return VIEWER_PREFIX + viewId;
4419    }
4420   
4421    /**
4422    * Returns any open frame that matches given structure viewer data. The match
4423    * is based on the unique viewId, or (for older project versions) the frame's
4424    * geometry.
4425    *
4426    * @param viewerData
4427    * @return
4428    */
 
4429  40 toggle protected StructureViewerBase findMatchingViewer(
4430    Entry<String, StructureViewerModel> viewerData)
4431    {
4432  40 final String sviewid = viewerData.getKey();
4433  40 final StructureViewerModel svattrib = viewerData.getValue();
4434  40 StructureViewerBase comp = null;
4435  40 JInternalFrame[] frames = getAllFrames();
4436  40 for (JInternalFrame frame : frames)
4437    {
4438  224 if (frame instanceof StructureViewerBase)
4439    {
4440    /*
4441    * Post jalview 2.4 schema includes structure view id
4442    */
4443  35 if (sviewid != null && ((StructureViewerBase) frame).getViewId()
4444    .equals(sviewid))
4445    {
4446  20 comp = (StructureViewerBase) frame;
4447  20 break; // break added in 2.9
4448    }
4449    /*
4450    * Otherwise test for matching position and size of viewer frame
4451    */
4452  15 else if (frame.getX() == svattrib.getX()
4453    && frame.getY() == svattrib.getY()
4454    && frame.getHeight() == svattrib.getHeight()
4455    && frame.getWidth() == svattrib.getWidth())
4456    {
4457  15 comp = (StructureViewerBase) frame;
4458    // no break in faint hope of an exact match on viewId
4459    }
4460    }
4461    }
4462  40 return comp;
4463    }
4464   
4465    /**
4466    * Link an AlignmentPanel to an existing structure viewer.
4467    *
4468    * @param ap
4469    * @param viewer
4470    * @param oldFiles
4471    * @param useinViewerSuperpos
4472    * @param usetoColourbyseq
4473    * @param viewerColouring
4474    */
 
4475  35 toggle protected void linkStructureViewer(AlignmentPanel ap,
4476    StructureViewerBase viewer, StructureViewerModel stateData)
4477    {
4478    // NOTE: if the jalview project is part of a shared session then
4479    // view synchronization should/could be done here.
4480   
4481  35 final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
4482  35 final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
4483  35 final boolean viewerColouring = stateData.isColourByViewer();
4484  35 Map<File, StructureData> oldFiles = stateData.getFileData();
4485   
4486    /*
4487    * Add mapping for sequences in this view to an already open viewer
4488    */
4489  35 final AAStructureBindingModel binding = viewer.getBinding();
4490  35 for (File id : oldFiles.keySet())
4491    {
4492    // add this and any other pdb files that should be present in the
4493    // viewer
4494  35 StructureData filedat = oldFiles.get(id);
4495  35 String pdbFile = filedat.getFilePath();
4496  35 SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
4497  35 binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
4498    null);
4499  35 binding.addSequenceForStructFile(pdbFile, seq);
4500    }
4501    // and add the AlignmentPanel's reference to the view panel
4502  35 viewer.addAlignmentPanel(ap);
4503  35 if (useinViewerSuperpos)
4504    {
4505  0 viewer.useAlignmentPanelForSuperposition(ap);
4506    }
4507    else
4508    {
4509  35 viewer.excludeAlignmentPanelForSuperposition(ap);
4510    }
4511  35 if (usetoColourbyseq)
4512    {
4513  8 viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
4514    }
4515    else
4516    {
4517  27 viewer.excludeAlignmentPanelForColourbyseq(ap);
4518    }
4519    }
4520   
4521    /**
4522    * Get all frames within the Desktop.
4523    *
4524    * @return
4525    */
 
4526  40 toggle protected JInternalFrame[] getAllFrames()
4527    {
4528  40 JInternalFrame[] frames = null;
4529    // TODO is this necessary - is it safe - risk of hanging?
4530  40 do
4531    {
4532  40 try
4533    {
4534  40 frames = Desktop.desktop.getAllFrames();
4535    } catch (ArrayIndexOutOfBoundsException e)
4536    {
4537    // occasional No such child exceptions are thrown here...
4538  0 try
4539    {
4540  0 Thread.sleep(10);
4541    } catch (InterruptedException f)
4542    {
4543    }
4544    }
4545  40 } while (frames == null);
4546  40 return frames;
4547    }
4548   
4549    /**
4550    * Answers true if 'version' is equal to or later than 'supported', where each
4551    * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
4552    * changes. Development and test values for 'version' are leniently treated
4553    * i.e. answer true.
4554    *
4555    * @param supported
4556    * - minimum version we are comparing against
4557    * @param version
4558    * - version of data being processsed
4559    * @return
4560    */
 
4561  97 toggle public static boolean isVersionStringLaterThan(String supported,
4562    String version)
4563    {
4564  97 if (supported == null || version == null
4565    || version.equalsIgnoreCase("DEVELOPMENT BUILD")
4566    || version.equalsIgnoreCase("Test")
4567    || version.equalsIgnoreCase("AUTOMATED BUILD"))
4568    {
4569  41 System.err.println("Assuming project file with "
4570  41 + (version == null ? "null" : version)
4571    + " is compatible with Jalview version " + supported);
4572  41 return true;
4573    }
4574    else
4575    {
4576  56 return StringUtils.compareVersions(version, supported, "b") >= 0;
4577    }
4578    }
4579   
4580    Vector<JalviewStructureDisplayI> newStructureViewers = null;
4581   
 
4582  5 toggle protected void addNewStructureViewer(JalviewStructureDisplayI sview)
4583    {
4584  5 if (newStructureViewers != null)
4585    {
4586  5 sview.getBinding().setFinishedLoadingFromArchive(false);
4587  5 newStructureViewers.add(sview);
4588    }
4589    }
4590   
 
4591  21 toggle protected void setLoadingFinishedForNewStructureViewers()
4592    {
4593  21 if (newStructureViewers != null)
4594    {
4595  21 for (JalviewStructureDisplayI sview : newStructureViewers)
4596    {
4597  5 sview.getBinding().setFinishedLoadingFromArchive(true);
4598    }
4599  21 newStructureViewers.clear();
4600  21 newStructureViewers = null;
4601    }
4602    }
4603   
 
4604  70 toggle AlignFrame loadViewport(String file, List<JSeq> JSEQ,
4605    List<SequenceI> hiddenSeqs, AlignmentI al,
4606    JalviewModel jm, Viewport view, String uniqueSeqSetId,
4607    String viewId, List<JvAnnotRow> autoAlan)
4608    {
4609  70 AlignFrame af = null;
4610  70 af = new AlignFrame(al, safeInt(view.getWidth()),
4611    safeInt(view.getHeight()), uniqueSeqSetId, viewId)
4612    // {
4613    //
4614    // @Override
4615    // protected void processKeyEvent(java.awt.event.KeyEvent e) {
4616    // System.out.println("Jalview2XML AF " + e);
4617    // super.processKeyEvent(e);
4618    //
4619    // }
4620    //
4621    // }
4622    ;
4623   
4624  70 af.setFileName(file, FileFormat.Jalview);
4625   
4626  70 final AlignViewport viewport = af.getViewport();
4627  1121 for (int i = 0; i < JSEQ.size(); i++)
4628    {
4629  1051 int colour = safeInt(JSEQ.get(i).getColour());
4630  1051 viewport.setSequenceColour(viewport.getAlignment().getSequenceAt(i),
4631    new Color(colour));
4632    }
4633   
4634  70 if (al.hasSeqrep())
4635    {
4636  5 viewport.setColourByReferenceSeq(true);
4637  5 viewport.setDisplayReferenceSeq(true);
4638    }
4639   
4640  70 viewport.setGatherViewsHere(safeBoolean(view.isGatheredViews()));
4641   
4642  70 if (view.getSequenceSetId() != null)
4643    {
4644  70 AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId);
4645   
4646  70 viewport.setSequenceSetId(uniqueSeqSetId);
4647  70 if (av != null)
4648    {
4649    // propagate shared settings to this new view
4650  41 viewport.setHistoryList(av.getHistoryList());
4651  41 viewport.setRedoList(av.getRedoList());
4652    }
4653    else
4654    {
4655  29 viewportsAdded.put(uniqueSeqSetId, viewport);
4656    }
4657    // TODO: check if this method can be called repeatedly without
4658    // side-effects if alignpanel already registered.
4659  70 PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
4660    }
4661    // apply Hidden regions to view.
4662  70 if (hiddenSeqs != null)
4663    {
4664  712 for (int s = 0; s < JSEQ.size(); s++)
4665    {
4666  670 SequenceGroup hidden = new SequenceGroup();
4667  670 boolean isRepresentative = false;
4668  672 for (int r = 0; r < JSEQ.get(s).getHiddenSequences().size(); r++)
4669    {
4670  2 isRepresentative = true;
4671  2 SequenceI sequenceToHide = al
4672    .getSequenceAt(JSEQ.get(s).getHiddenSequences().get(r));
4673  2 hidden.addSequence(sequenceToHide, false);
4674    // remove from hiddenSeqs list so we don't try to hide it twice
4675  2 hiddenSeqs.remove(sequenceToHide);
4676    }
4677  670 if (isRepresentative)
4678    {
4679  2 SequenceI representativeSequence = al.getSequenceAt(s);
4680  2 hidden.addSequence(representativeSequence, false);
4681  2 viewport.hideRepSequences(representativeSequence, hidden);
4682    }
4683    }
4684   
4685  42 SequenceI[] hseqs = hiddenSeqs
4686    .toArray(new SequenceI[hiddenSeqs.size()]);
4687  42 viewport.hideSequence(hseqs);
4688   
4689    }
4690    // recover view properties and display parameters
4691   
4692  70 viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation()));
4693  70 viewport.setAbovePIDThreshold(safeBoolean(view.isPidSelected()));
4694  70 final int pidThreshold = safeInt(view.getPidThreshold());
4695  70 viewport.setThreshold(pidThreshold);
4696   
4697  70 viewport.setColourText(safeBoolean(view.isShowColourText()));
4698   
4699  70 viewport
4700    .setConservationSelected(
4701    safeBoolean(view.isConservationSelected()));
4702  70 viewport.setIncrement(safeInt(view.getConsThreshold()));
4703  70 viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
4704  70 viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
4705  70 viewport.setFont(new Font(view.getFontName(),
4706    safeInt(view.getFontStyle()), safeInt(view.getFontSize())),
4707    true);
4708  70 ViewStyleI vs = viewport.getViewStyle();
4709  70 vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna());
4710  70 viewport.setViewStyle(vs);
4711    // TODO: allow custom charWidth/Heights to be restored by updating them
4712    // after setting font - which means set above to false
4713  70 viewport.setRenderGaps(safeBoolean(view.isRenderGaps()));
4714  70 viewport.setWrapAlignment(safeBoolean(view.isWrapAlignment()));
4715  70 viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation()));
4716   
4717  70 viewport.setShowBoxes(safeBoolean(view.isShowBoxes()));
4718   
4719  70 viewport.setShowText(safeBoolean(view.isShowText()));
4720   
4721  70 viewport.setTextColour(new Color(safeInt(view.getTextCol1())));
4722  70 viewport.setTextColour2(new Color(safeInt(view.getTextCol2())));
4723  70 viewport.setThresholdTextColour(safeInt(view.getTextColThreshold()));
4724  70 viewport.setShowUnconserved(view.isShowUnconserved());
4725  70 viewport.getRanges().setStartRes(safeInt(view.getStartRes()));
4726   
4727  70 if (view.getViewName() != null)
4728    {
4729  55 viewport.setViewName(view.getViewName());
4730  55 af.setInitialTabVisible();
4731    }
4732  70 af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
4733    safeInt(view.getWidth()), safeInt(view.getHeight()));
4734    // startSeq set in af.alignPanel.updateLayout below
4735  70 af.alignPanel.updateLayout();
4736  70 ColourSchemeI cs = null;
4737    // apply colourschemes
4738  70 if (view.getBgColour() != null)
4739    {
4740  70 if (view.getBgColour().startsWith("ucs"))
4741    {
4742  0 cs = getUserColourScheme(jm, view.getBgColour());
4743    }
4744  70 else if (view.getBgColour().startsWith("Annotation"))
4745    {
4746  1 AnnotationColourScheme viewAnnColour = view.getAnnotationColours();
4747  1 cs = constructAnnotationColour(viewAnnColour, af, al, jm, true);
4748   
4749    // annpos
4750   
4751    }
4752    else
4753    {
4754  69 cs = ColourSchemeProperty.getColourScheme(af.getViewport(), al,
4755    view.getBgColour());
4756    }
4757    }
4758   
4759    /*
4760    * turn off 'alignment colour applies to all groups'
4761    * while restoring global colour scheme
4762    */
4763  70 viewport.setColourAppliesToAllGroups(false);
4764  70 viewport.setGlobalColourScheme(cs);
4765  70 viewport.getResidueShading().setThreshold(pidThreshold,
4766    view.isIgnoreGapsinConsensus());
4767  70 viewport.getResidueShading()
4768    .setConsensus(viewport.getSequenceConsensusHash());
4769  70 if (safeBoolean(view.isConservationSelected()) && cs != null)
4770    {
4771  2 viewport.getResidueShading()
4772    .setConservationInc(safeInt(view.getConsThreshold()));
4773    }
4774  70 af.changeColour(cs);
4775  70 viewport.setColourAppliesToAllGroups(true);
4776   
4777  70 viewport
4778    .setShowSequenceFeatures(
4779    safeBoolean(view.isShowSequenceFeatures()));
4780   
4781  70 viewport.setCentreColumnLabels(view.isCentreColumnLabels());
4782  70 viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null);
4783  70 viewport.setFollowHighlight(view.isFollowHighlight());
4784  70 viewport.followSelection = view.isFollowSelection();
4785  70 viewport.setShowConsensusHistogram(view.isShowConsensusHistogram());
4786  70 viewport.setShowSequenceLogo(view.isShowSequenceLogo());
4787  70 viewport.setNormaliseSequenceLogo(view.isNormaliseSequenceLogo());
4788  70 viewport.setShowDBRefs(safeBoolean(view.isShowDbRefTooltip()));
4789  70 viewport.setShowNPFeats(safeBoolean(view.isShowNPfeatureTooltip()));
4790  70 viewport.setShowGroupConsensus(view.isShowGroupConsensus());
4791  70 viewport.setShowGroupConservation(view.isShowGroupConservation());
4792  70 viewport.setShowComplementFeatures(view.isShowComplementFeatures());
4793  70 viewport.setShowComplementFeaturesOnTop(
4794    view.isShowComplementFeaturesOnTop());
4795   
4796    // recover feature settings
4797  70 if (jm.getFeatureSettings() != null)
4798    {
4799  36 FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas
4800    .getFeatureRenderer();
4801  36 FeaturesDisplayed fdi;
4802  36 viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
4803  36 String[] renderOrder = new String[jm.getFeatureSettings()
4804    .getSetting().size()];
4805  36 Map<String, FeatureColourI> featureColours = new Hashtable<>();
4806  36 Map<String, Float> featureOrder = new Hashtable<>();
4807   
4808  870 for (int fs = 0; fs < jm.getFeatureSettings()
4809    .getSetting().size(); fs++)
4810    {
4811  834 Setting setting = jm.getFeatureSettings().getSetting().get(fs);
4812  834 String featureType = setting.getType();
4813   
4814    /*
4815    * restore feature filters (if any)
4816    */
4817  834 jalview.xml.binding.jalview.FeatureMatcherSet filters = setting
4818    .getMatcherSet();
4819  834 if (filters != null)
4820    {
4821  3 FeatureMatcherSetI filter = Jalview2XML
4822    .parseFilter(featureType, filters);
4823  3 if (!filter.isEmpty())
4824    {
4825  3 fr.setFeatureFilter(featureType, filter);
4826    }
4827    }
4828   
4829    /*
4830    * restore feature colour scheme
4831    */
4832  834 Color maxColour = new Color(setting.getColour());
4833  834 if (setting.getMincolour() != null)
4834    {
4835    /*
4836    * minColour is always set unless a simple colour
4837    * (including for colour by label though it doesn't use it)
4838    */
4839  5 Color minColour = new Color(setting.getMincolour().intValue());
4840  5 Color noValueColour = minColour;
4841  5 NoValueColour noColour = setting.getNoValueColour();
4842  5 if (noColour == NoValueColour.NONE)
4843    {
4844  5 noValueColour = null;
4845    }
4846  0 else if (noColour == NoValueColour.MAX)
4847    {
4848  0 noValueColour = maxColour;
4849    }
4850  5 float min = safeFloat(safeFloat(setting.getMin()));
4851  5 float max = setting.getMax() == null ? 1f
4852    : setting.getMax().floatValue();
4853  5 FeatureColourI gc = new FeatureColour(maxColour, minColour,
4854    maxColour,
4855    noValueColour, min, max);
4856  5 if (setting.getAttributeName().size() > 0)
4857    {
4858  2 gc.setAttributeName(setting.getAttributeName().toArray(
4859    new String[setting.getAttributeName().size()]));
4860    }
4861  5 if (setting.getThreshold() != null)
4862    {
4863  5 gc.setThreshold(setting.getThreshold().floatValue());
4864  5 int threshstate = safeInt(setting.getThreshstate());
4865    // -1 = None, 0 = Below, 1 = Above threshold
4866  5 if (threshstate == 0)
4867    {
4868  1 gc.setBelowThreshold(true);
4869    }
4870  4 else if (threshstate == 1)
4871    {
4872  1 gc.setAboveThreshold(true);
4873    }
4874    }
4875  5 gc.setAutoScaled(true); // default
4876  5 if (setting.isAutoScale() != null)
4877    {
4878  5 gc.setAutoScaled(setting.isAutoScale());
4879    }
4880  5 if (setting.isColourByLabel() != null)
4881    {
4882  5 gc.setColourByLabel(setting.isColourByLabel());
4883    }
4884    // and put in the feature colour table.
4885  5 featureColours.put(featureType, gc);
4886    }
4887    else
4888    {
4889  829 featureColours.put(featureType,
4890    new FeatureColour(maxColour));
4891    }
4892  834 renderOrder[fs] = featureType;
4893  834 if (setting.getOrder() != null)
4894    {
4895  834 featureOrder.put(featureType, setting.getOrder().floatValue());
4896    }
4897    else
4898    {
4899  0 featureOrder.put(featureType, Float.valueOf(
4900    fs / jm.getFeatureSettings().getSetting().size()));
4901    }
4902  834 if (safeBoolean(setting.isDisplay()))
4903    {
4904  241 fdi.setVisible(featureType);
4905    }
4906    }
4907  36 Map<String, Boolean> fgtable = new Hashtable<>();
4908  163 for (int gs = 0; gs < jm.getFeatureSettings().getGroup().size(); gs++)
4909    {
4910  127 Group grp = jm.getFeatureSettings().getGroup().get(gs);
4911  127 fgtable.put(grp.getName(), Boolean.valueOf(grp.isDisplay()));
4912    }
4913    // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
4914    // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
4915    // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
4916  36 FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
4917    fgtable, featureColours, 1.0f, featureOrder);
4918  36 fr.transferSettings(frs);
4919    }
4920   
4921  70 if (view.getHiddenColumns().size() > 0)
4922    {
4923  76 for (int c = 0; c < view.getHiddenColumns().size(); c++)
4924    {
4925  54 final HiddenColumns hc = view.getHiddenColumns().get(c);
4926  54 viewport.hideColumns(safeInt(hc.getStart()),
4927    safeInt(hc.getEnd()) /* +1 */);
4928    }
4929    }
4930  70 if (view.getCalcIdParam() != null)
4931    {
4932  70 for (CalcIdParam calcIdParam : view.getCalcIdParam())
4933    {
4934  0 if (calcIdParam != null)
4935    {
4936  0 if (recoverCalcIdParam(calcIdParam, viewport))
4937    {
4938    }
4939    else
4940    {
4941  0 warn("Couldn't recover parameters for "
4942    + calcIdParam.getCalcId());
4943    }
4944    }
4945    }
4946    }
4947  70 af.setMenusFromViewport(viewport);
4948  70 af.setTitle(view.getTitle());
4949    // TODO: we don't need to do this if the viewport is aready visible.
4950    /*
4951    * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
4952    * has a 'cdna/protein complement' view, in which case save it in order to
4953    * populate a SplitFrame once all views have been read in.
4954    */
4955  70 String complementaryViewId = view.getComplementId();
4956  70 if (complementaryViewId == null)
4957    {
4958  70 Desktop.addInternalFrame(af, view.getTitle(),
4959    safeInt(view.getWidth()), safeInt(view.getHeight()));
4960    // recompute any autoannotation
4961  70 af.alignPanel.updateAnnotation(false, true);
4962  70 reorderAutoannotation(af, al, autoAlan);
4963  70 af.alignPanel.alignmentChanged();
4964    }
4965    else
4966    {
4967  0 splitFrameCandidates.put(view, af);
4968    }
4969  70 return af;
4970    }
4971   
4972    /**
4973    * Reads saved data to restore Colour by Annotation settings
4974    *
4975    * @param viewAnnColour
4976    * @param af
4977    * @param al
4978    * @param model
4979    * @param checkGroupAnnColour
4980    * @return
4981    */
 
4982  2 toggle private ColourSchemeI constructAnnotationColour(
4983    AnnotationColourScheme viewAnnColour, AlignFrame af,
4984    AlignmentI al, JalviewModel model, boolean checkGroupAnnColour)
4985    {
4986  2 boolean propagateAnnColour = false;
4987  2 AlignmentI annAlignment = af != null ? af.getViewport().getAlignment()
4988    : al;
4989  2 if (checkGroupAnnColour && al.getGroups() != null
4990    && al.getGroups().size() > 0)
4991    {
4992    // pre 2.8.1 behaviour
4993    // check to see if we should transfer annotation colours
4994  1 propagateAnnColour = true;
4995  1 for (SequenceGroup sg : al.getGroups())
4996    {
4997  1 if (sg.getColourScheme() instanceof AnnotationColourGradient)
4998    {
4999  1 propagateAnnColour = false;
5000    }
5001    }
5002    }
5003   
5004    /*
5005    * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId
5006    */
5007  2 String annotationId = viewAnnColour.getAnnotation();
5008  2 AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId);
5009   
5010    /*
5011    * pre 2.10.2: saved annotationId is AlignmentAnnotation.label
5012    */
5013  2 if (matchedAnnotation == null
5014    && annAlignment.getAlignmentAnnotation() != null)
5015    {
5016  0 for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
5017    {
5018  0 if (annotationId
5019    .equals(annAlignment.getAlignmentAnnotation()[i].label))
5020    {
5021  0 matchedAnnotation = annAlignment.getAlignmentAnnotation()[i];
5022  0 break;
5023    }
5024    }
5025    }
5026  2 if (matchedAnnotation == null)
5027    {
5028  0 System.err.println("Failed to match annotation colour scheme for "
5029    + annotationId);
5030  0 return null;
5031    }
5032  2 if (matchedAnnotation.getThreshold() == null)
5033    {
5034  0 matchedAnnotation.setThreshold(
5035    new GraphLine(safeFloat(viewAnnColour.getThreshold()),
5036    "Threshold", Color.black));
5037    }
5038   
5039  2 AnnotationColourGradient cs = null;
5040  2 if (viewAnnColour.getColourScheme().equals("None"))
5041    {
5042  2 cs = new AnnotationColourGradient(matchedAnnotation,
5043    new Color(safeInt(viewAnnColour.getMinColour())),
5044    new Color(safeInt(viewAnnColour.getMaxColour())),
5045    safeInt(viewAnnColour.getAboveThreshold()));
5046    }
5047  0 else if (viewAnnColour.getColourScheme().startsWith("ucs"))
5048    {
5049  0 cs = new AnnotationColourGradient(matchedAnnotation,
5050    getUserColourScheme(model, viewAnnColour.getColourScheme()),
5051    safeInt(viewAnnColour.getAboveThreshold()));
5052    }
5053    else
5054    {
5055  0 cs = new AnnotationColourGradient(matchedAnnotation,
5056    ColourSchemeProperty.getColourScheme(af.getViewport(), al,
5057    viewAnnColour.getColourScheme()),
5058    safeInt(viewAnnColour.getAboveThreshold()));
5059    }
5060   
5061  2 boolean perSequenceOnly = safeBoolean(viewAnnColour.isPerSequence());
5062  2 boolean useOriginalColours = safeBoolean(
5063    viewAnnColour.isPredefinedColours());
5064  2 cs.setSeqAssociated(perSequenceOnly);
5065  2 cs.setPredefinedColours(useOriginalColours);
5066   
5067  2 if (propagateAnnColour && al.getGroups() != null)
5068    {
5069    // Also use these settings for all the groups
5070  0 for (int g = 0; g < al.getGroups().size(); g++)
5071    {
5072  0 SequenceGroup sg = al.getGroups().get(g);
5073  0 if (sg.getGroupColourScheme() == null)
5074    {
5075  0 continue;
5076    }
5077   
5078  0 AnnotationColourGradient groupScheme = new AnnotationColourGradient(
5079    matchedAnnotation, sg.getColourScheme(),
5080    safeInt(viewAnnColour.getAboveThreshold()));
5081  0 sg.setColourScheme(groupScheme);
5082  0 groupScheme.setSeqAssociated(perSequenceOnly);
5083  0 groupScheme.setPredefinedColours(useOriginalColours);
5084    }
5085    }
5086  2 return cs;
5087    }
5088   
 
5089  70 toggle private void reorderAutoannotation(AlignFrame af, AlignmentI al,
5090    List<JvAnnotRow> autoAlan)
5091    {
5092    // copy over visualization settings for autocalculated annotation in the
5093    // view
5094  70 if (al.getAlignmentAnnotation() != null)
5095    {
5096    /**
5097    * Kludge for magic autoannotation names (see JAL-811)
5098    */
5099  70 String[] magicNames = new String[] { "Consensus", "Quality",
5100    "Conservation" };
5101  70 JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
5102  70 Hashtable<String, JvAnnotRow> visan = new Hashtable<>();
5103  70 for (String nm : magicNames)
5104    {
5105  210 visan.put(nm, nullAnnot);
5106    }
5107  70 for (JvAnnotRow auan : autoAlan)
5108    {
5109  238 visan.put(auan.template.label
5110  238 + (auan.template.getCalcId() == null ? ""
5111    : "\t" + auan.template.getCalcId()),
5112    auan);
5113    }
5114  70 int hSize = al.getAlignmentAnnotation().length;
5115  70 List<JvAnnotRow> reorder = new ArrayList<>();
5116    // work through any autoCalculated annotation already on the view
5117    // removing it if it should be placed in a different location on the
5118    // annotation panel.
5119  70 List<String> remains = new ArrayList<>(visan.keySet());
5120  562 for (int h = 0; h < hSize; h++)
5121    {
5122  492 jalview.datamodel.AlignmentAnnotation jalan = al
5123    .getAlignmentAnnotation()[h];
5124  492 if (jalan.autoCalculated)
5125    {
5126  297 String k;
5127  297 JvAnnotRow valan = visan.get(k = jalan.label);
5128  297 if (jalan.getCalcId() != null)
5129    {
5130  273 valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
5131    }
5132   
5133  297 if (valan != null)
5134    {
5135    // delete the auto calculated row from the alignment
5136  166 al.deleteAnnotation(jalan, false);
5137  166 remains.remove(k);
5138  166 hSize--;
5139  166 h--;
5140  166 if (valan != nullAnnot)
5141    {
5142  166 if (jalan != valan.template)
5143    {
5144    // newly created autoannotation row instance
5145    // so keep a reference to the visible annotation row
5146    // and copy over all relevant attributes
5147  142 if (valan.template.graphHeight >= 0)
5148   
5149    {
5150  142 jalan.graphHeight = valan.template.graphHeight;
5151    }
5152  142 jalan.visible = valan.template.visible;
5153    }
5154  166 reorder.add(new JvAnnotRow(valan.order, jalan));
5155    }
5156    }
5157    }
5158    }
5159    // Add any (possibly stale) autocalculated rows that were not appended to
5160    // the view during construction
5161  70 for (String other : remains)
5162    {
5163  210 JvAnnotRow othera = visan.get(other);
5164  210 if (othera != nullAnnot && othera.template.getCalcId() != null
5165    && othera.template.getCalcId().length() > 0)
5166    {
5167  0 reorder.add(othera);
5168    }
5169    }
5170    // now put the automatic annotation in its correct place
5171  70 int s = 0, srt[] = new int[reorder.size()];
5172  70 JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
5173  70 for (JvAnnotRow jvar : reorder)
5174    {
5175  166 rws[s] = jvar;
5176  166 srt[s++] = jvar.order;
5177    }
5178  70 reorder.clear();
5179  70 jalview.util.QuickSort.sort(srt, rws);
5180    // and re-insert the annotation at its correct position
5181  70 for (JvAnnotRow jvar : rws)
5182    {
5183  166 al.addAnnotation(jvar.template, jvar.order);
5184    }
5185  70 af.alignPanel.adjustAnnotationHeight();
5186    }
5187    }
5188   
5189    Hashtable skipList = null;
5190   
5191    /**
5192    * TODO remove this method
5193    *
5194    * @param view
5195    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
5196    * AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
5197    * throw new Error("Implementation Error. No skipList defined for this
5198    * Jalview2XML instance."); } return (AlignFrame)
5199    * skipList.get(view.getSequenceSetId()); }
5200    */
5201   
5202    /**
5203    * Check if the Jalview view contained in object should be skipped or not.
5204    *
5205    * @param object
5206    * @return true if view's sequenceSetId is a key in skipList
5207    */
 
5208  0 toggle private boolean skipViewport(JalviewModel object)
5209    {
5210  0 if (skipList == null)
5211    {
5212  0 return false;
5213    }
5214  0 String id = object.getViewport().get(0).getSequenceSetId();
5215  0 if (skipList.containsKey(id))
5216    {
5217  0 if (Cache.log != null && Cache.log.isDebugEnabled())
5218    {
5219  0 Cache.log.debug("Skipping seuqence set id " + id);
5220    }
5221  0 return true;
5222    }
5223  0 return false;
5224    }
5225   
 
5226  0 toggle public void addToSkipList(AlignFrame af)
5227    {
5228  0 if (skipList == null)
5229    {
5230  0 skipList = new Hashtable();
5231    }
5232  0 skipList.put(af.getViewport().getSequenceSetId(), af);
5233    }
5234   
 
5235  0 toggle public void clearSkipList()
5236    {
5237  0 if (skipList != null)
5238    {
5239  0 skipList.clear();
5240  0 skipList = null;
5241    }
5242    }
5243   
 
5244  56 toggle private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
5245    boolean ignoreUnrefed, String uniqueSeqSetId)
5246    {
5247  56 jalview.datamodel.AlignmentI ds = getDatasetFor(
5248    vamsasSet.getDatasetId());
5249  56 AlignmentI xtant_ds = ds;
5250  56 if (xtant_ds == null)
5251    {
5252    // good chance we are about to create a new dataset, but check if we've
5253    // seen some of the dataset sequence IDs before.
5254    // TODO: skip this check if we are working with project generated by
5255    // version 2.11 or later
5256  29 xtant_ds = checkIfHasDataset(vamsasSet.getSequence());
5257  29 if (xtant_ds != null)
5258    {
5259  2 ds = xtant_ds;
5260  2 addDatasetRef(vamsasSet.getDatasetId(), ds);
5261    }
5262    }
5263  56 Vector<SequenceI> dseqs = null;
5264  56 if (!ignoreUnrefed)
5265    {
5266    // recovering an alignment View
5267  40 AlignmentI seqSetDS = getDatasetFor(UNIQSEQSETID + uniqueSeqSetId);
5268  40 if (seqSetDS != null)
5269    {
5270  17 if (ds != null && ds != seqSetDS)
5271    {
5272  0 warn("JAL-3171 regression: Overwriting a dataset reference for an alignment"
5273    + " - CDS/Protein crossreference data may be lost");
5274  0 if (xtant_ds != null)
5275    {
5276    // This can only happen if the unique sequence set ID was bound to a
5277    // dataset that did not contain any of the sequences in the view
5278    // currently being restored.
5279  0 warn("JAL-3171 SERIOUS! TOTAL CONFUSION - please consider contacting the Jalview Development team so they can investigate why your project caused this message to be displayed.");
5280    }
5281    }
5282  17 ds = seqSetDS;
5283  17 addDatasetRef(vamsasSet.getDatasetId(), ds);
5284    }
5285    }
5286  56 if (ds == null)
5287    {
5288    // try even harder to restore dataset
5289  15 AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence());
5290    // create a list of new dataset sequences
5291  15 dseqs = new Vector<>();
5292    }
5293  627 for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++)
5294    {
5295  571 Sequence vamsasSeq = vamsasSet.getSequence().get(i);
5296  571 ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i);
5297    }
5298    // create a new dataset
5299  56 if (ds == null)
5300    {
5301  15 SequenceI[] dsseqs = new SequenceI[dseqs.size()];
5302  15 dseqs.copyInto(dsseqs);
5303  15 ds = new jalview.datamodel.Alignment(dsseqs);
5304  15 debug("Created new dataset " + vamsasSet.getDatasetId()
5305    + " for alignment " + System.identityHashCode(al));
5306  15 addDatasetRef(vamsasSet.getDatasetId(), ds);
5307    }
5308    // set the dataset for the newly imported alignment.
5309  56 if (al.getDataset() == null && !ignoreUnrefed)
5310    {
5311  40 al.setDataset(ds);
5312    // register dataset for the alignment's uniqueSeqSetId for legacy projects
5313  40 addDatasetRef(UNIQSEQSETID + uniqueSeqSetId, ds);
5314    }
5315  56 updateSeqDatasetBinding(vamsasSet.getSequence(), ds);
5316    }
5317   
5318    /**
5319    * XML dataset sequence ID to materialised dataset reference
5320    */
5321    HashMap<String, AlignmentI> seqToDataset = new HashMap<>();
5322   
5323    /**
5324    * @return the first materialised dataset reference containing a dataset
5325    * sequence referenced in the given view
5326    * @param list
5327    * - sequences from the view
5328    */
 
5329  44 toggle AlignmentI checkIfHasDataset(List<Sequence> list)
5330    {
5331  44 for (Sequence restoredSeq : list)
5332    {
5333  450 AlignmentI datasetFor = seqToDataset.get(restoredSeq.getDsseqid());
5334  450 if (datasetFor != null)
5335    {
5336  2 return datasetFor;
5337    }
5338    }
5339  42 return null;
5340    }
5341   
5342    /**
5343    * Register ds as the containing dataset for the dataset sequences referenced
5344    * by sequences in list
5345    *
5346    * @param list
5347    * - sequences in a view
5348    * @param ds
5349    */
 
5350  56 toggle void updateSeqDatasetBinding(List<Sequence> list, AlignmentI ds)
5351    {
5352  56 for (Sequence restoredSeq : list)
5353    {
5354  571 AlignmentI prevDS = seqToDataset.put(restoredSeq.getDsseqid(), ds);
5355  571 if (prevDS != null && prevDS != ds)
5356    {
5357  0 warn("Dataset sequence appears in many datasets: "
5358    + restoredSeq.getDsseqid());
5359    // TODO: try to merge!
5360    }
5361    }
5362    }
5363    /**
5364    *
5365    * @param vamsasSeq
5366    * sequence definition to create/merge dataset sequence for
5367    * @param ds
5368    * dataset alignment
5369    * @param dseqs
5370    * vector to add new dataset sequence to
5371    * @param ignoreUnrefed
5372    * - when true, don't create new sequences from vamsasSeq if it's id
5373    * doesn't already have an asssociated Jalview sequence.
5374    * @param vseqpos
5375    * - used to reorder the sequence in the alignment according to the
5376    * vamsasSeq array ordering, to preserve ordering of dataset
5377    */
 
5378  571 toggle private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
5379    AlignmentI ds, Vector<SequenceI> dseqs, boolean ignoreUnrefed,
5380    int vseqpos)
5381    {
5382    // JBP TODO: Check this is called for AlCodonFrames to support recovery of
5383    // xRef Codon Maps
5384  571 SequenceI sq = seqRefIds.get(vamsasSeq.getId());
5385  571 boolean reorder = false;
5386  571 SequenceI dsq = null;
5387  571 if (sq != null && sq.getDatasetSequence() != null)
5388    {
5389  76 dsq = sq.getDatasetSequence();
5390    }
5391    else
5392    {
5393  495 reorder = true;
5394    }
5395  571 if (sq == null && ignoreUnrefed)
5396    {
5397  0 return;
5398    }
5399  571 String sqid = vamsasSeq.getDsseqid();
5400  571 if (dsq == null)
5401    {
5402    // need to create or add a new dataset sequence reference to this sequence
5403  495 if (sqid != null)
5404    {
5405  495 dsq = seqRefIds.get(sqid);
5406    }
5407    // check again
5408  495 if (dsq == null)
5409    {
5410    // make a new dataset sequence
5411  230 dsq = sq.createDatasetSequence();
5412  230 if (sqid == null)
5413    {
5414    // make up a new dataset reference for this sequence
5415  0 sqid = seqHash(dsq);
5416    }
5417  230 dsq.setVamsasId(uniqueSetSuffix + sqid);
5418  230 seqRefIds.put(sqid, dsq);
5419  230 if (ds == null)
5420    {
5421  223 if (dseqs != null)
5422    {
5423  223 dseqs.addElement(dsq);
5424    }
5425    }
5426    else
5427    {
5428  7 ds.addSequence(dsq);
5429    }
5430    }
5431    else
5432    {
5433  265 if (sq != dsq)
5434    { // make this dataset sequence sq's dataset sequence
5435  16 sq.setDatasetSequence(dsq);
5436    // and update the current dataset alignment
5437  16 if (ds == null)
5438    {
5439  0 if (dseqs != null)
5440    {
5441  0 if (!dseqs.contains(dsq))
5442    {
5443  0 dseqs.add(dsq);
5444    }
5445    }
5446    else
5447    {
5448  0 if (ds.findIndex(dsq) < 0)
5449    {
5450  0 ds.addSequence(dsq);
5451    }
5452    }
5453    }
5454    }
5455    }
5456    }
5457    // TODO: refactor this as a merge dataset sequence function
5458    // now check that sq (the dataset sequence) sequence really is the union of
5459    // all references to it
5460    // boolean pre = sq.getStart() < dsq.getStart();
5461    // boolean post = sq.getEnd() > dsq.getEnd();
5462    // if (pre || post)
5463  571 if (sq != dsq)
5464    {
5465    // StringBuffer sb = new StringBuffer();
5466  322 String newres = jalview.analysis.AlignSeq.extractGaps(
5467    jalview.util.Comparison.GapChars, sq.getSequenceAsString());
5468  322 if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
5469    && newres.length() > dsq.getLength())
5470    {
5471    // Update with the longer sequence.
5472  8 synchronized (dsq)
5473    {
5474    /*
5475    * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
5476    * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
5477    * sb.append(newres.substring(newres.length() - sq.getEnd() -
5478    * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
5479    */
5480  8 dsq.setSequence(newres);
5481    }
5482    // TODO: merges will never happen if we 'know' we have the real dataset
5483    // sequence - this should be detected when id==dssid
5484  8 System.err.println(
5485    "DEBUG Notice: Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
5486    // + (pre ? "prepended" : "") + " "
5487    // + (post ? "appended" : ""));
5488    }
5489    }
5490    else
5491    {
5492    // sequence refs are identical. We may need to update the existing dataset
5493    // alignment with this one, though.
5494  249 if (ds != null && dseqs == null)
5495    {
5496  249 int opos = ds.findIndex(dsq);
5497  249 SequenceI tseq = null;
5498  249 if (opos != -1 && vseqpos != opos)
5499    {
5500    // remove from old position
5501  39 ds.deleteSequence(dsq);
5502    }
5503  249 if (vseqpos < ds.getHeight())
5504    {
5505  249 if (vseqpos != opos)
5506    {
5507    // save sequence at destination position
5508  50 tseq = ds.getSequenceAt(vseqpos);
5509  50 ds.replaceSequenceAt(vseqpos, dsq);
5510  50 ds.addSequence(tseq);
5511    }
5512    }
5513    else
5514    {
5515  0 ds.addSequence(dsq);
5516    }
5517    }
5518    }
5519    }
5520   
5521    /*
5522    * TODO use AlignmentI here and in related methods - needs
5523    * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment
5524    */
5525    Hashtable<String, AlignmentI> datasetIds = null;
5526   
5527    IdentityHashMap<AlignmentI, String> dataset2Ids = null;
5528   
 
5529  112 toggle private AlignmentI getDatasetFor(String datasetId)
5530    {
5531  112 if (datasetIds == null)
5532    {
5533  15 datasetIds = new Hashtable<>();
5534  15 return null;
5535    }
5536  97 if (datasetIds.containsKey(datasetId))
5537    {
5538  60 return datasetIds.get(datasetId);
5539    }
5540  37 return null;
5541    }
5542   
 
5543  96 toggle private void addDatasetRef(String datasetId, AlignmentI dataset)
5544    {
5545  96 if (datasetIds == null)
5546    {
5547  6 datasetIds = new Hashtable<>();
5548    }
5549  96 datasetIds.put(datasetId, dataset);
5550    }
5551   
5552    /**
5553    * make a new dataset ID for this jalview dataset alignment
5554    *
5555    * @param dataset
5556    * @return
5557    */
 
5558  68 toggle private String getDatasetIdRef(AlignmentI dataset)
5559    {
5560  68 if (dataset.getDataset() != null)
5561    {
5562  0 warn("Serious issue! Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
5563    }
5564  68 String datasetId = makeHashCode(dataset, null);
5565  68 if (datasetId == null)
5566    {
5567    // make a new datasetId and record it
5568  68 if (dataset2Ids == null)
5569    {
5570  18 dataset2Ids = new IdentityHashMap<>();
5571    }
5572    else
5573    {
5574  50 datasetId = dataset2Ids.get(dataset);
5575    }
5576  68 if (datasetId == null)
5577    {
5578  26 datasetId = "ds" + dataset2Ids.size() + 1;
5579  26 dataset2Ids.put(dataset, datasetId);
5580    }
5581    }
5582  68 return datasetId;
5583    }
5584   
5585    /**
5586    * Add any saved DBRefEntry's to the sequence. An entry flagged as 'locus' is
5587    * constructed as a special subclass GeneLocus.
5588    *
5589    * @param datasetSequence
5590    * @param sequence
5591    */
 
5592  222 toggle private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
5593    {
5594  1313 for (int d = 0; d < sequence.getDBRef().size(); d++)
5595    {
5596  1091 DBRef dr = sequence.getDBRef().get(d);
5597  1091 DBRefEntry entry;
5598  1091 if (dr.isLocus())
5599    {
5600  23 entry = new GeneLocus(dr.getSource(), dr.getVersion(),
5601    dr.getAccessionId());
5602    }
5603    else
5604    {
5605  1068 entry = new DBRefEntry(dr.getSource(), dr.getVersion(),
5606    dr.getAccessionId());
5607    }
5608  1091 if (dr.getMapping() != null)
5609    {
5610  46 entry.setMap(addMapping(dr.getMapping()));
5611    }
5612  1091 datasetSequence.addDBRef(entry);
5613    }
5614    }
5615   
 
5616  46 toggle private jalview.datamodel.Mapping addMapping(Mapping m)
5617    {
5618  46 SequenceI dsto = null;
5619    // Mapping m = dr.getMapping();
5620  46 int fr[] = new int[m.getMapListFrom().size() * 2];
5621  46 Iterator<MapListFrom> from = m.getMapListFrom().iterator();// enumerateMapListFrom();
5622  92 for (int _i = 0; from.hasNext(); _i += 2)
5623    {
5624  46 MapListFrom mf = from.next();
5625  46 fr[_i] = mf.getStart();
5626  46 fr[_i + 1] = mf.getEnd();
5627    }
5628  46 int fto[] = new int[m.getMapListTo().size() * 2];
5629  46 Iterator<MapListTo> to = m.getMapListTo().iterator();// enumerateMapListTo();
5630  313 for (int _i = 0; to.hasNext(); _i += 2)
5631    {
5632  267 MapListTo mf = to.next();
5633  267 fto[_i] = mf.getStart();
5634  267 fto[_i + 1] = mf.getEnd();
5635    }
5636  46 jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto, fr,
5637    fto, m.getMapFromUnit().intValue(),
5638    m.getMapToUnit().intValue());
5639   
5640    /*
5641    * (optional) choice of dseqFor or Sequence
5642    */
5643  46 if (m.getDseqFor() != null)
5644    {
5645  23 String dsfor = m.getDseqFor();
5646  23 if (seqRefIds.containsKey(dsfor))
5647    {
5648    /*
5649    * recover from hash
5650    */
5651  23 jmap.setTo(seqRefIds.get(dsfor));
5652    }
5653    else
5654    {
5655  0 frefedSequence.add(newMappingRef(dsfor, jmap));
5656    }
5657    }
5658  23 else if (m.getSequence() != null)
5659    {
5660    /*
5661    * local sequence definition
5662    */
5663  0 Sequence ms = m.getSequence();
5664  0 SequenceI djs = null;
5665  0 String sqid = ms.getDsseqid();
5666  0 if (sqid != null && sqid.length() > 0)
5667    {
5668    /*
5669    * recover dataset sequence
5670    */
5671  0 djs = seqRefIds.get(sqid);
5672    }
5673    else
5674    {
5675  0 System.err.println(
5676    "Warning - making up dataset sequence id for DbRef sequence map reference");
5677  0 sqid = ((Object) ms).toString(); // make up a new hascode for
5678    // undefined dataset sequence hash
5679    // (unlikely to happen)
5680    }
5681   
5682  0 if (djs == null)
5683    {
5684    /**
5685    * make a new dataset sequence and add it to refIds hash
5686    */
5687  0 djs = new jalview.datamodel.Sequence(ms.getName(),
5688    ms.getSequence());
5689  0 djs.setStart(jmap.getMap().getToLowest());
5690  0 djs.setEnd(jmap.getMap().getToHighest());
5691  0 djs.setVamsasId(uniqueSetSuffix + sqid);
5692  0 jmap.setTo(djs);
5693  0 incompleteSeqs.put(sqid, djs);
5694  0 seqRefIds.put(sqid, djs);
5695   
5696    }
5697  0 jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
5698  0 addDBRefs(djs, ms);
5699   
5700    }
5701   
5702  46 return jmap;
5703    }
5704   
5705    /**
5706    * Provides a 'copy' of an alignment view (on action New View) by 'saving' the
5707    * view as XML (but not to file), and then reloading it
5708    *
5709    * @param ap
5710    * @return
5711    */
 
5712  6 toggle public AlignmentPanel copyAlignPanel(AlignmentPanel ap)
5713    {
5714  6 initSeqRefs();
5715  6 JalviewModel jm = saveState(ap, null, null, null);
5716   
5717  6 addDatasetRef(
5718    jm.getVamsasModel().getSequenceSet().get(0).getDatasetId(),
5719    ap.getAlignment().getDataset());
5720   
5721  6 uniqueSetSuffix = "";
5722    // jm.getJalviewModelSequence().getViewport(0).setId(null);
5723  6 jm.getViewport().get(0).setId(null);
5724    // we don't overwrite the view we just copied
5725   
5726  6 if (this.frefedSequence == null)
5727    {
5728  0 frefedSequence = new Vector<>();
5729    }
5730   
5731  6 viewportsAdded.clear();
5732   
5733  6 AlignFrame af = loadFromObject(jm, null, false, null);
5734  6 af.getAlignPanels().clear();
5735  6 af.closeMenuItem_actionPerformed(true);
5736   
5737    /*
5738    * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
5739    * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
5740    * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
5741    * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
5742    * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
5743    */
5744   
5745  6 return af.alignPanel;
5746    }
5747   
5748    private Hashtable jvids2vobj;
5749   
 
5750  0 toggle private void warn(String msg)
5751    {
5752  0 warn(msg, null);
5753    }
5754   
 
5755  0 toggle private void warn(String msg, Exception e)
5756    {
5757  0 if (Cache.log != null)
5758    {
5759  0 if (e != null)
5760    {
5761  0 Cache.log.warn(msg, e);
5762    }
5763    else
5764    {
5765  0 Cache.log.warn(msg);
5766    }
5767    }
5768    else
5769    {
5770  0 System.err.println("Warning: " + msg);
5771  0 if (e != null)
5772    {
5773  0 e.printStackTrace();
5774    }
5775    }
5776    }
5777   
 
5778  15 toggle private void debug(String string)
5779    {
5780  15 debug(string, null);
5781    }
5782   
 
5783  15 toggle private void debug(String msg, Exception e)
5784    {
5785  15 if (Cache.log != null)
5786    {
5787  15 if (e != null)
5788    {
5789  0 Cache.log.debug(msg, e);
5790    }
5791    else
5792    {
5793  15 Cache.log.debug(msg);
5794    }
5795    }
5796    else
5797    {
5798  0 System.err.println("Warning: " + msg);
5799  0 if (e != null)
5800    {
5801  0 e.printStackTrace();
5802    }
5803    }
5804    }
5805   
5806    /**
5807    * set the object to ID mapping tables used to write/recover objects and XML
5808    * ID strings for the jalview project. If external tables are provided then
5809    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
5810    * object goes out of scope. - also populates the datasetIds hashtable with
5811    * alignment objects containing dataset sequences
5812    *
5813    * @param vobj2jv
5814    * Map from ID strings to jalview datamodel
5815    * @param jv2vobj
5816    * Map from jalview datamodel to ID strings
5817    *
5818    *
5819    */
 
5820  0 toggle public void setObjectMappingTables(Hashtable vobj2jv,
5821    IdentityHashMap jv2vobj)
5822    {
5823  0 this.jv2vobj = jv2vobj;
5824  0 this.vobj2jv = vobj2jv;
5825  0 Iterator ds = jv2vobj.keySet().iterator();
5826  0 String id;
5827  0 while (ds.hasNext())
5828    {
5829  0 Object jvobj = ds.next();
5830  0 id = jv2vobj.get(jvobj).toString();
5831  0 if (jvobj instanceof jalview.datamodel.Alignment)
5832    {
5833  0 if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
5834    {
5835  0 addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
5836    }
5837    }
5838  0 else if (jvobj instanceof jalview.datamodel.Sequence)
5839    {
5840    // register sequence object so the XML parser can recover it.
5841  0 if (seqRefIds == null)
5842    {
5843  0 seqRefIds = new HashMap<>();
5844    }
5845  0 if (seqsToIds == null)
5846    {
5847  0 seqsToIds = new IdentityHashMap<>();
5848    }
5849  0 seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
5850  0 seqsToIds.put((SequenceI) jvobj, id);
5851    }
5852  0 else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
5853    {
5854  0 String anid;
5855  0 AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj;
5856  0 annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann);
5857  0 if (jvann.annotationId == null)
5858    {
5859  0 jvann.annotationId = anid;
5860    }
5861  0 if (!jvann.annotationId.equals(anid))
5862    {
5863    // TODO verify that this is the correct behaviour
5864  0 this.warn("Overriding Annotation ID for " + anid
5865    + " from different id : " + jvann.annotationId);
5866  0 jvann.annotationId = anid;
5867    }
5868    }
5869  0 else if (jvobj instanceof String)
5870    {
5871  0 if (jvids2vobj == null)
5872    {
5873  0 jvids2vobj = new Hashtable();
5874  0 jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
5875    }
5876    }
5877    else
5878    {
5879  0 Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
5880    }
5881    }
5882    }
5883   
5884    /**
5885    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
5886    * objects created from the project archive. If string is null (default for
5887    * construction) then suffix will be set automatically.
5888    *
5889    * @param string
5890    */
 
5891  0 toggle public void setUniqueSetSuffix(String string)
5892    {
5893  0 uniqueSetSuffix = string;
5894   
5895    }
5896   
5897    /**
5898    * uses skipList2 as the skipList for skipping views on sequence sets
5899    * associated with keys in the skipList
5900    *
5901    * @param skipList2
5902    */
 
5903  0 toggle public void setSkipList(Hashtable skipList2)
5904    {
5905  0 skipList = skipList2;
5906    }
5907   
5908    /**
5909    * Reads the jar entry of given name and returns its contents, or null if the
5910    * entry is not found.
5911    *
5912    * @param jprovider
5913    * @param jarEntryName
5914    * @return
5915    */
 
5916  2 toggle protected String readJarEntry(jarInputStreamProvider jprovider,
5917    String jarEntryName)
5918    {
5919  2 String result = null;
5920  2 BufferedReader in = null;
5921   
5922  2 try
5923    {
5924    /*
5925    * Reopen the jar input stream and traverse its entries to find a matching
5926    * name
5927    */
5928  2 JarInputStream jin = jprovider.getJarInputStream();
5929  2 JarEntry entry = null;
5930  2 do
5931    {
5932  2 entry = jin.getNextJarEntry();
5933  2 } while (entry != null && !entry.getName().equals(jarEntryName));
5934   
5935  2 if (entry != null)
5936    {
5937  2 StringBuilder out = new StringBuilder(256);
5938  2 in = new BufferedReader(new InputStreamReader(jin, UTF_8));
5939  2 String data;
5940   
5941  ? while ((data = in.readLine()) != null)
5942    {
5943  0 out.append(data);
5944    }
5945  2 result = out.toString();
5946    }
5947    else
5948    {
5949  0 warn("Couldn't find entry in Jalview Jar for " + jarEntryName);
5950    }
5951    } catch (Exception ex)
5952    {
5953  0 ex.printStackTrace();
5954    } finally
5955    {
5956  2 if (in != null)
5957    {
5958  2 try
5959    {
5960  2 in.close();
5961    } catch (IOException e)
5962    {
5963    // ignore
5964    }
5965    }
5966    }
5967   
5968  2 return result;
5969    }
5970   
5971    /**
5972    * Returns an incrementing counter (0, 1, 2...)
5973    *
5974    * @return
5975    */
 
5976  0 toggle private synchronized int nextCounter()
5977    {
5978  0 return counter++;
5979    }
5980   
5981    /**
5982    * Loads any saved PCA viewers
5983    *
5984    * @param jms
5985    * @param ap
5986    */
 
5987  64 toggle protected void loadPCAViewers(JalviewModel model, AlignmentPanel ap)
5988    {
5989  64 try
5990    {
5991  64 List<PcaViewer> pcaviewers = model.getPcaViewer();
5992  64 for (PcaViewer viewer : pcaviewers)
5993    {
5994  1 String modelName = viewer.getScoreModelName();
5995  1 SimilarityParamsI params = new SimilarityParams(
5996    viewer.isIncludeGappedColumns(), viewer.isMatchGaps(),
5997    viewer.isIncludeGaps(),
5998    viewer.isDenominateByShortestLength());
5999   
6000    /*
6001    * create the panel (without computing the PCA)
6002    */
6003  1 PCAPanel panel = new PCAPanel(ap, modelName, params);
6004   
6005  1 panel.setTitle(viewer.getTitle());
6006  1 panel.setBounds(new Rectangle(viewer.getXpos(), viewer.getYpos(),
6007    viewer.getWidth(), viewer.getHeight()));
6008   
6009  1 boolean showLabels = viewer.isShowLabels();
6010  1 panel.setShowLabels(showLabels);
6011  1 panel.getRotatableCanvas().setShowLabels(showLabels);
6012  1 panel.getRotatableCanvas()
6013    .setBgColour(new Color(viewer.getBgColour()));
6014  1 panel.getRotatableCanvas()
6015    .setApplyToAllViews(viewer.isLinkToAllViews());
6016   
6017    /*
6018    * load PCA output data
6019    */
6020  1 ScoreModelI scoreModel = ScoreModels.getInstance()
6021    .getScoreModel(modelName, ap);
6022  1 PCA pca = new PCA(null, scoreModel, params);
6023  1 PcaDataType pcaData = viewer.getPcaData();
6024   
6025  1 MatrixI pairwise = loadDoubleMatrix(pcaData.getPairwiseMatrix());
6026  1 pca.setPairwiseScores(pairwise);
6027   
6028  1 MatrixI triDiag = loadDoubleMatrix(pcaData.getTridiagonalMatrix());
6029  1 pca.setTridiagonal(triDiag);
6030   
6031  1 MatrixI result = loadDoubleMatrix(pcaData.getEigenMatrix());
6032  1 pca.setEigenmatrix(result);
6033   
6034  1 panel.getPcaModel().setPCA(pca);
6035   
6036    /*
6037    * we haven't saved the input data! (JAL-2647 to do)
6038    */
6039  1 panel.setInputData(null);
6040   
6041    /*
6042    * add the sequence points for the PCA display
6043    */
6044  1 List<jalview.datamodel.SequencePoint> seqPoints = new ArrayList<>();
6045  1 for (SequencePoint sp : viewer.getSequencePoint())
6046    {
6047  15 String seqId = sp.getSequenceRef();
6048  15 SequenceI seq = seqRefIds.get(seqId);
6049  15 if (seq == null)
6050    {
6051  0 throw new IllegalStateException(
6052    "Unmatched seqref for PCA: " + seqId);
6053    }
6054  15 Point pt = new Point(sp.getXPos(), sp.getYPos(), sp.getZPos());
6055  15 jalview.datamodel.SequencePoint seqPoint = new jalview.datamodel.SequencePoint(
6056    seq, pt);
6057  15 seqPoints.add(seqPoint);
6058    }
6059  1 panel.getRotatableCanvas().setPoints(seqPoints, seqPoints.size());
6060   
6061    /*
6062    * set min-max ranges and scale after setPoints (which recomputes them)
6063    */
6064  1 panel.getRotatableCanvas().setScaleFactor(viewer.getScaleFactor());
6065  1 SeqPointMin spMin = viewer.getSeqPointMin();
6066  1 float[] min = new float[] { spMin.getXPos(), spMin.getYPos(),
6067    spMin.getZPos() };
6068  1 SeqPointMax spMax = viewer.getSeqPointMax();
6069  1 float[] max = new float[] { spMax.getXPos(), spMax.getYPos(),
6070    spMax.getZPos() };
6071  1 panel.getRotatableCanvas().setSeqMinMax(min, max);
6072   
6073    // todo: hold points list in PCAModel only
6074  1 panel.getPcaModel().setSequencePoints(seqPoints);
6075   
6076  1 panel.setSelectedDimensionIndex(viewer.getXDim(), X);
6077  1 panel.setSelectedDimensionIndex(viewer.getYDim(), Y);
6078  1 panel.setSelectedDimensionIndex(viewer.getZDim(), Z);
6079   
6080    // is this duplication needed?
6081  1 panel.setTop(seqPoints.size() - 1);
6082  1 panel.getPcaModel().setTop(seqPoints.size() - 1);
6083   
6084    /*
6085    * add the axes' end points for the display
6086    */
6087  4 for (int i = 0; i < 3; i++)
6088    {
6089  3 Axis axis = viewer.getAxis().get(i);
6090  3 panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point(
6091    axis.getXPos(), axis.getYPos(), axis.getZPos());
6092    }
6093   
6094  1 Desktop.addInternalFrame(panel, MessageManager.formatMessage(
6095    "label.calc_title", "PCA", modelName), 475, 450);
6096    }
6097    } catch (Exception ex)
6098    {
6099  0 Cache.log.error("Error loading PCA: " + ex.toString());
6100    }
6101    }
6102   
6103    /**
6104    * Creates a new structure viewer window
6105    *
6106    * @param viewerType
6107    * @param viewerData
6108    * @param af
6109    * @param jprovider
6110    */
 
6111  5 toggle protected void createStructureViewer(
6112    ViewerType viewerType, final Entry<String, StructureViewerModel> viewerData,
6113    AlignFrame af, jarInputStreamProvider jprovider)
6114    {
6115  5 final StructureViewerModel viewerModel = viewerData.getValue();
6116  5 String sessionFilePath = null;
6117   
6118  5 if (viewerType == ViewerType.JMOL)
6119    {
6120  5 sessionFilePath = rewriteJmolSession(viewerModel, jprovider);
6121    }
6122    else
6123    {
6124  0 String viewerJarEntryName = getViewerJarEntryName(
6125    viewerModel.getViewId());
6126  0 sessionFilePath = copyJarEntry(jprovider,
6127    viewerJarEntryName,
6128    "viewerSession", ".tmp");
6129    }
6130  5 final String sessionPath = sessionFilePath;
6131  5 final String sviewid = viewerData.getKey();
6132  5 try
6133    {
6134  5 SwingUtilities.invokeAndWait(new Runnable()
6135    {
 
6136  5 toggle @Override
6137    public void run()
6138    {
6139  5 JalviewStructureDisplayI sview = null;
6140  5 try
6141    {
6142  5 sview = StructureViewer.createView(viewerType, af.alignPanel,
6143    viewerModel, sessionPath, sviewid);
6144  5 addNewStructureViewer(sview);
6145    } catch (OutOfMemoryError ex)
6146    {
6147  0 new OOMWarning("Restoring structure view for "
6148    + viewerType,
6149    (OutOfMemoryError) ex.getCause());
6150  0 if (sview != null && sview.isVisible())
6151    {
6152  0 sview.closeViewer(false);
6153  0 sview.setVisible(false);
6154  0 sview.dispose();
6155    }
6156    }
6157    }
6158    });
6159    } catch (InvocationTargetException | InterruptedException ex)
6160    {
6161  0 warn("Unexpected error when opening " + viewerType
6162    + " structure viewer", ex);
6163    }
6164    }
6165   
6166    /**
6167    * Rewrites a Jmol session script, saves it to a temporary file, and returns
6168    * the path of the file. "load file" commands are rewritten to change the
6169    * original PDB file names to those created as the Jalview project is loaded.
6170    *
6171    * @param svattrib
6172    * @param jprovider
6173    * @return
6174    */
 
6175  5 toggle private String rewriteJmolSession(StructureViewerModel svattrib,
6176    jarInputStreamProvider jprovider)
6177    {
6178  5 String state = svattrib.getStateData(); // Jalview < 2.9
6179  5 if (state == null || state.isEmpty()) // Jalview >= 2.9
6180    {
6181  2 String jarEntryName = getViewerJarEntryName(svattrib.getViewId());
6182  2 state = readJarEntry(jprovider, jarEntryName);
6183    }
6184    // TODO or simpler? for each key in oldFiles,
6185    // replace key.getPath() in state with oldFiles.get(key).getFilePath()
6186    // (allowing for different path escapings)
6187  5 StringBuilder rewritten = new StringBuilder(state.length());
6188  5 int cp = 0, ncp, ecp;
6189  5 Map<File, StructureData> oldFiles = svattrib.getFileData();
6190  ? while ((ncp = state.indexOf("load ", cp)) > -1)
6191    {
6192  3 do
6193    {
6194    // look for next filename in load statement
6195  3 rewritten.append(state.substring(cp,
6196    ncp = (state.indexOf("\"", ncp + 1) + 1)));
6197  3 String oldfilenam = state.substring(ncp,
6198    ecp = state.indexOf("\"", ncp));
6199    // recover the new mapping data for this old filename
6200    // have to normalize filename - since Jmol and jalview do
6201    // filename translation differently.
6202  3 StructureData filedat = oldFiles.get(new File(oldfilenam));
6203  3 if (filedat == null)
6204    {
6205  0 String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
6206  0 filedat = oldFiles.get(new File(reformatedOldFilename));
6207    }
6208  3 rewritten
6209    .append(Platform.escapeBackslashes(filedat.getFilePath()));
6210  3 rewritten.append("\"");
6211  3 cp = ecp + 1; // advance beyond last \" and set cursor so we can
6212    // look for next file statement.
6213  ? } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
6214    }
6215  5 if (cp > 0)
6216    {
6217    // just append rest of state
6218  3 rewritten.append(state.substring(cp));
6219    }
6220    else
6221    {
6222  2 System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
6223  2 rewritten = new StringBuilder(state);
6224  2 rewritten.append("; load append ");
6225  2 for (File id : oldFiles.keySet())
6226    {
6227    // add pdb files that should be present in the viewer
6228  2 StructureData filedat = oldFiles.get(id);
6229  2 rewritten.append(" \"").append(filedat.getFilePath()).append("\"");
6230    }
6231  2 rewritten.append(";");
6232    }
6233   
6234  5 if (rewritten.length() == 0)
6235    {
6236  0 return null;
6237    }
6238  5 final String history = "history = ";
6239  5 int historyIndex = rewritten.indexOf(history);
6240  5 if (historyIndex > -1)
6241    {
6242    /*
6243    * change "history = [true|false];" to "history = [1|0];"
6244    */
6245  0 historyIndex += history.length();
6246  0 String val = rewritten.substring(historyIndex, historyIndex + 5);
6247  0 if (val.startsWith("true"))
6248    {
6249  0 rewritten.replace(historyIndex, historyIndex + 4, "1");
6250    }
6251  0 else if (val.startsWith("false"))
6252    {
6253  0 rewritten.replace(historyIndex, historyIndex + 5, "0");
6254    }
6255    }
6256   
6257  5 try
6258    {
6259  5 File tmp = File.createTempFile("viewerSession", ".tmp");
6260  5 try (OutputStream os = new FileOutputStream(tmp))
6261    {
6262  5 InputStream is = new ByteArrayInputStream(
6263    rewritten.toString().getBytes());
6264  5 copyAll(is, os);
6265  5 return tmp.getAbsolutePath();
6266    }
6267    } catch (IOException e)
6268    {
6269  0 Cache.log.error("Error restoring Jmol session: " + e.toString());
6270    }
6271  0 return null;
6272    }
6273   
6274    /**
6275    * Populates an XML model of the feature colour scheme for one feature type
6276    *
6277    * @param featureType
6278    * @param fcol
6279    * @return
6280    */
 
6281  5 toggle public static Colour marshalColour(
6282    String featureType, FeatureColourI fcol)
6283    {
6284  5 Colour col = new Colour();
6285  5 if (fcol.isSimpleColour())
6286    {
6287  1 col.setRGB(Format.getHexString(fcol.getColour()));
6288    }
6289    else
6290    {
6291  4 col.setRGB(Format.getHexString(fcol.getMaxColour()));
6292  4 col.setMin(fcol.getMin());
6293  4 col.setMax(fcol.getMax());
6294  4 col.setMinRGB(jalview.util.Format.getHexString(fcol.getMinColour()));
6295  4 col.setAutoScale(fcol.isAutoScaled());
6296  4 col.setThreshold(fcol.getThreshold());
6297  4 col.setColourByLabel(fcol.isColourByLabel());
6298  4 col.setThreshType(fcol.isAboveThreshold() ? ThresholdType.ABOVE
6299  3 : (fcol.isBelowThreshold() ? ThresholdType.BELOW
6300    : ThresholdType.NONE));
6301  4 if (fcol.isColourByAttribute())
6302    {
6303  2 final String[] attName = fcol.getAttributeName();
6304  2 col.getAttributeName().add(attName[0]);
6305  2 if (attName.length > 1)
6306    {
6307  1 col.getAttributeName().add(attName[1]);
6308    }
6309    }
6310  4 Color noColour = fcol.getNoColour();
6311  4 if (noColour == null)
6312    {
6313  4 col.setNoValueColour(NoValueColour.NONE);
6314    }
6315  0 else if (noColour == fcol.getMaxColour())
6316    {
6317  0 col.setNoValueColour(NoValueColour.MAX);
6318    }
6319    else
6320    {
6321  0 col.setNoValueColour(NoValueColour.MIN);
6322    }
6323    }
6324  5 col.setName(featureType);
6325  5 return col;
6326    }
6327   
6328    /**
6329    * Populates an XML model of the feature filter(s) for one feature type
6330    *
6331    * @param firstMatcher
6332    * the first (or only) match condition)
6333    * @param filter
6334    * remaining match conditions (if any)
6335    * @param and
6336    * if true, conditions are and-ed, else or-ed
6337    */
 
6338  14 toggle public static jalview.xml.binding.jalview.FeatureMatcherSet marshalFilter(
6339    FeatureMatcherI firstMatcher, Iterator<FeatureMatcherI> filters,
6340    boolean and)
6341    {
6342  14 jalview.xml.binding.jalview.FeatureMatcherSet result = new jalview.xml.binding.jalview.FeatureMatcherSet();
6343   
6344  14 if (filters.hasNext())
6345    {
6346    /*
6347    * compound matcher
6348    */
6349  4 CompoundMatcher compound = new CompoundMatcher();
6350  4 compound.setAnd(and);
6351  4 jalview.xml.binding.jalview.FeatureMatcherSet matcher1 = marshalFilter(
6352    firstMatcher, Collections.emptyIterator(), and);
6353    // compound.addMatcherSet(matcher1);
6354  4 compound.getMatcherSet().add(matcher1);
6355  4 FeatureMatcherI nextMatcher = filters.next();
6356  4 jalview.xml.binding.jalview.FeatureMatcherSet matcher2 = marshalFilter(
6357    nextMatcher, filters, and);
6358    // compound.addMatcherSet(matcher2);
6359  4 compound.getMatcherSet().add(matcher2);
6360  4 result.setCompoundMatcher(compound);
6361    }
6362    else
6363    {
6364    /*
6365    * single condition matcher
6366    */
6367    // MatchCondition matcherModel = new MatchCondition();
6368  10 jalview.xml.binding.jalview.FeatureMatcher matcherModel = new jalview.xml.binding.jalview.FeatureMatcher();
6369  10 matcherModel.setCondition(
6370    firstMatcher.getMatcher().getCondition().getStableName());
6371  10 matcherModel.setValue(firstMatcher.getMatcher().getPattern());
6372  10 if (firstMatcher.isByAttribute())
6373    {
6374  4 matcherModel.setBy(FilterBy.BY_ATTRIBUTE);
6375    // matcherModel.setAttributeName(firstMatcher.getAttribute());
6376  4 String[] attName = firstMatcher.getAttribute();
6377  4 matcherModel.getAttributeName().add(attName[0]); // attribute
6378  4 if (attName.length > 1)
6379    {
6380  2 matcherModel.getAttributeName().add(attName[1]); // sub-attribute
6381    }
6382    }
6383  6 else if (firstMatcher.isByLabel())
6384    {
6385  2 matcherModel.setBy(FilterBy.BY_LABEL);
6386    }
6387  4 else if (firstMatcher.isByScore())
6388    {
6389  4 matcherModel.setBy(FilterBy.BY_SCORE);
6390    }
6391  10 result.setMatchCondition(matcherModel);
6392    }
6393   
6394  14 return result;
6395    }
6396   
6397    /**
6398    * Loads one XML model of a feature filter to a Jalview object
6399    *
6400    * @param featureType
6401    * @param matcherSetModel
6402    * @return
6403    */
 
6404  6 toggle public static FeatureMatcherSetI parseFilter(
6405    String featureType,
6406    jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel)
6407    {
6408  6 FeatureMatcherSetI result = new FeatureMatcherSet();
6409  6 try
6410    {
6411  6 parseFilterConditions(result, matcherSetModel, true);
6412    } catch (IllegalStateException e)
6413    {
6414    // mixing AND and OR conditions perhaps
6415  0 System.err.println(
6416    String.format("Error reading filter conditions for '%s': %s",
6417    featureType, e.getMessage()));
6418    // return as much as was parsed up to the error
6419    }
6420   
6421  6 return result;
6422    }
6423   
6424    /**
6425    * Adds feature match conditions to matcherSet as unmarshalled from XML
6426    * (possibly recursively for compound conditions)
6427    *
6428    * @param matcherSet
6429    * @param matcherSetModel
6430    * @param and
6431    * if true, multiple conditions are AND-ed, else they are OR-ed
6432    * @throws IllegalStateException
6433    * if AND and OR conditions are mixed
6434    */
 
6435  14 toggle protected static void parseFilterConditions(
6436    FeatureMatcherSetI matcherSet,
6437    jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel,
6438    boolean and)
6439    {
6440  14 jalview.xml.binding.jalview.FeatureMatcher mc = matcherSetModel
6441    .getMatchCondition();
6442  14 if (mc != null)
6443    {
6444    /*
6445    * single condition
6446    */
6447  10 FilterBy filterBy = mc.getBy();
6448  10 Condition cond = Condition.fromString(mc.getCondition());
6449  10 String pattern = mc.getValue();
6450  10 FeatureMatcherI matchCondition = null;
6451  10 if (filterBy == FilterBy.BY_LABEL)
6452    {
6453  2 matchCondition = FeatureMatcher.byLabel(cond, pattern);
6454    }
6455  8 else if (filterBy == FilterBy.BY_SCORE)
6456    {
6457  4 matchCondition = FeatureMatcher.byScore(cond, pattern);
6458   
6459    }
6460  4 else if (filterBy == FilterBy.BY_ATTRIBUTE)
6461    {
6462  4 final List<String> attributeName = mc.getAttributeName();
6463  4 String[] attNames = attributeName
6464    .toArray(new String[attributeName.size()]);
6465  4 matchCondition = FeatureMatcher.byAttribute(cond, pattern,
6466    attNames);
6467    }
6468   
6469    /*
6470    * note this throws IllegalStateException if AND-ing to a
6471    * previously OR-ed compound condition, or vice versa
6472    */
6473  10 if (and)
6474    {
6475  6 matcherSet.and(matchCondition);
6476    }
6477    else
6478    {
6479  4 matcherSet.or(matchCondition);
6480    }
6481    }
6482    else
6483    {
6484    /*
6485    * compound condition
6486    */
6487  4 List<jalview.xml.binding.jalview.FeatureMatcherSet> matchers = matcherSetModel
6488    .getCompoundMatcher().getMatcherSet();
6489  4 boolean anded = matcherSetModel.getCompoundMatcher().isAnd();
6490  4 if (matchers.size() == 2)
6491    {
6492  4 parseFilterConditions(matcherSet, matchers.get(0), anded);
6493  4 parseFilterConditions(matcherSet, matchers.get(1), anded);
6494    }
6495    else
6496    {
6497  0 System.err.println("Malformed compound filter condition");
6498    }
6499    }
6500    }
6501   
6502    /**
6503    * Loads one XML model of a feature colour to a Jalview object
6504    *
6505    * @param colourModel
6506    * @return
6507    */
 
6508  5 toggle public static FeatureColourI parseColour(Colour colourModel)
6509    {
6510  5 FeatureColourI colour = null;
6511   
6512  5 if (colourModel.getMax() != null)
6513    {
6514  4 Color mincol = null;
6515  4 Color maxcol = null;
6516  4 Color noValueColour = null;
6517   
6518  4 try
6519    {
6520  4 mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16));
6521  4 maxcol = new Color(Integer.parseInt(colourModel.getRGB(), 16));
6522    } catch (Exception e)
6523    {
6524  0 Cache.log.warn("Couldn't parse out graduated feature color.", e);
6525    }
6526   
6527  4 NoValueColour noCol = colourModel.getNoValueColour();
6528  4 if (noCol == NoValueColour.MIN)
6529    {
6530  0 noValueColour = mincol;
6531    }
6532  4 else if (noCol == NoValueColour.MAX)
6533    {
6534  0 noValueColour = maxcol;
6535    }
6536   
6537  4 colour = new FeatureColour(maxcol, mincol, maxcol, noValueColour,
6538    safeFloat(colourModel.getMin()),
6539    safeFloat(colourModel.getMax()));
6540  4 final List<String> attributeName = colourModel.getAttributeName();
6541  4 String[] attributes = attributeName
6542    .toArray(new String[attributeName.size()]);
6543  4 if (attributes != null && attributes.length > 0)
6544    {
6545  2 colour.setAttributeName(attributes);
6546    }
6547  4 if (colourModel.isAutoScale() != null)
6548    {
6549  4 colour.setAutoScaled(colourModel.isAutoScale().booleanValue());
6550    }
6551  4 if (colourModel.isColourByLabel() != null)
6552    {
6553  4 colour.setColourByLabel(
6554    colourModel.isColourByLabel().booleanValue());
6555    }
6556  4 if (colourModel.getThreshold() != null)
6557    {
6558  4 colour.setThreshold(colourModel.getThreshold().floatValue());
6559    }
6560  4 ThresholdType ttyp = colourModel.getThreshType();
6561  4 if (ttyp == ThresholdType.ABOVE)
6562    {
6563  1 colour.setAboveThreshold(true);
6564    }
6565  3 else if (ttyp == ThresholdType.BELOW)
6566    {
6567  1 colour.setBelowThreshold(true);
6568    }
6569    }
6570    else
6571    {
6572  1 Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16));
6573  1 colour = new FeatureColour(color);
6574    }
6575   
6576  5 return colour;
6577    }
6578    }