Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,154
2,639
120
4
7,352
5,353
827
0.31
21.99
30
6.89

Classes

Class Line # Actions
Jalview2XML 230 2,625 817
0.74350474.4%
Jalview2XML.forwardRef 403 4 3
0.4285714342.9%
Jalview2XML.SeqFref 439 8 6
0.00%
Jalview2XML.JvAnnotRow 3675 2 1
1.0100%
 

Contributing tests

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