Clover icon

Coverage Report

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

File Jalview2XML.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
33% of files have more coverage

Code metrics

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

Classes

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

Contributing tests

This file is covered by 15 tests. .

Source view

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