Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:56:54 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,168
2,669
121
4
7,430
5,420
837
0.31
22.06
30.25
6.92

Classes

Class Line # Actions
Jalview2XML 230 2,655 827
0.743387674.3%
Jalview2XML.forwardRef 403 4 3
0.4285714342.9%
Jalview2XML.SeqFref 439 8 6
0.00%
Jalview2XML.JvAnnotRow 3716 2 1
1.0100%
 

Contributing tests

This file is covered by 40 tests. .

Source view

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