1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,164
2,663
120
4
7,415
5,407
834
0.31
22.19
30
6.95

Classes

Class
Line #
Actions
Jalview2XML 230 2,649 824
0.741902674.2%
Jalview2XML.forwardRef 403 4 3
0.4285714342.9%
Jalview2XML.SeqFref 439 8 6
0.00%
Jalview2XML.JvAnnotRow 3701 2 1
1.0100%
 

Contributing tests

This file is covered by 41 tests. .

Source view

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