Clover icon

Coverage Report

  1. Project Clover database Tue Mar 10 2026 14:58:44 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
0% of files have more coverage

Code metrics

1,226
2,793
130
4
7,826
5,678
879
0.31
21.48
32.5
6.76

Classes

Class Line # Actions
Jalview2XML 235 2,779 869
0.00%
Jalview2XML.forwardRef 429 4 3
0.00%
Jalview2XML.SeqFref 465 8 6
0.00%
Jalview2XML.JvAnnotRow 3940 2 1
0.00%
 

Contributing tests

No tests hitting this source file were found.

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