Clover icon

Coverage Report

  1. Project Clover database Wed Nov 13 2024 16:12:26 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,158
2,645
121
4
7,367
5,366
830
0.31
21.86
30.25
6.86

Classes

Class Line # Actions
Jalview2XML 230 2,631 820
0.7414058474.1%
Jalview2XML.forwardRef 403 4 3
0.4285714342.9%
Jalview2XML.SeqFref 439 8 6
0.00%
Jalview2XML.JvAnnotRow 3690 2 1
1.0100%
 

Contributing tests

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