Clover icon

Coverage Report

  1. Project Clover database Thu Nov 7 2024 17:01:39 GMT
  2. Package jalview.project

File Jalview2XML.java

 

Coverage histogram

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

Code metrics

1,154
2,639
120
4
7,352
5,353
827
0.31
21.99
30
6.89

Classes

Class Line # Actions
Jalview2XML 230 2,625 817
0.00%
Jalview2XML.forwardRef 403 4 3
0.00%
Jalview2XML.SeqFref 439 8 6
0.00%
Jalview2XML.JvAnnotRow 3675 2 1
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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