Clover icon

Coverage Report

  1. Project Clover database Wed Sep 17 2025 10:52:37 BST
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,196
2,722
122
4
7,550
5,512
853
0.31
22.31
30.5
6.99

Classes

Class Line # Actions
Jalview2XML 231 2,708 843
0.7491280474.9%
Jalview2XML.forwardRef 404 4 3
0.4285714342.9%
Jalview2XML.SeqFref 440 8 6
0.00%
Jalview2XML.JvAnnotRow 3790 2 1
1.0100%
 

Contributing tests

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