Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,220
2,781
130
4
7,807
5,660
875
0.31
21.39
32.5
6.73

Classes

Class Line # Actions
Jalview2XML 235 2,767 865
0.7529841775.3%
Jalview2XML.forwardRef 429 4 3
0.4285714342.9%
Jalview2XML.SeqFref 465 8 6
0.00%
Jalview2XML.JvAnnotRow 3933 2 1
1.0100%
 

Contributing tests

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