2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.project;
23 import static jalview.math.RotatableMatrix.Axis.X;
24 import static jalview.math.RotatableMatrix.Axis.Y;
25 import static jalview.math.RotatableMatrix.Axis.Z;
27 import java.awt.Color;
29 import java.awt.Rectangle;
30 import java.io.BufferedReader;
31 import java.io.ByteArrayInputStream;
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;
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;
60 import java.util.Map.Entry;
62 import java.util.Vector;
63 import java.util.jar.JarEntry;
64 import java.util.jar.JarInputStream;
65 import java.util.jar.JarOutputStream;
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;
78 import jalview.analysis.Conservation;
79 import jalview.analysis.PCA;
80 import jalview.analysis.scoremodels.ScoreModels;
81 import jalview.analysis.scoremodels.SimilarityParams;
82 import jalview.api.FeatureColourI;
83 import jalview.api.ViewStyleI;
84 import jalview.api.analysis.ScoreModelI;
85 import jalview.api.analysis.SimilarityParamsI;
86 import jalview.api.structures.JalviewStructureDisplayI;
87 import jalview.bin.Cache;
88 import jalview.bin.Console;
89 import jalview.bin.Jalview;
90 import jalview.datamodel.AlignedCodonFrame;
91 import jalview.datamodel.Alignment;
92 import jalview.datamodel.AlignmentAnnotation;
93 import jalview.datamodel.AlignmentI;
94 import jalview.datamodel.ContactListI;
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.GroupSetI;
103 import jalview.datamodel.PDBEntry;
104 import jalview.datamodel.Point;
105 import jalview.datamodel.RnaViewerModel;
106 import jalview.datamodel.SequenceFeature;
107 import jalview.datamodel.SequenceGroup;
108 import jalview.datamodel.SequenceI;
109 import jalview.datamodel.StructureViewerModel;
110 import jalview.datamodel.StructureViewerModel.StructureData;
111 import jalview.datamodel.features.FeatureMatcher;
112 import jalview.datamodel.features.FeatureMatcherI;
113 import jalview.datamodel.features.FeatureMatcherSet;
114 import jalview.datamodel.features.FeatureMatcherSetI;
115 import jalview.ext.varna.RnaModel;
116 import jalview.gui.AlignFrame;
117 import jalview.gui.AlignViewport;
118 import jalview.gui.AlignmentPanel;
119 import jalview.gui.AppVarna;
120 import jalview.gui.Desktop;
121 import jalview.gui.JvOptionPane;
122 import jalview.gui.OOMWarning;
123 import jalview.gui.OverviewPanel;
124 import jalview.gui.PCAPanel;
125 import jalview.gui.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.ViewportRanges;
156 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
157 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
158 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
159 import jalview.ws.datamodel.MappableContactMatrixI;
160 import jalview.ws.datamodel.alphafold.PAEContactMatrix;
161 import jalview.ws.jws2.Jws2Discoverer;
162 import jalview.ws.jws2.dm.AAConSettings;
163 import jalview.ws.jws2.jabaws2.Jws2Instance;
164 import jalview.ws.params.ArgumentI;
165 import jalview.ws.params.AutoCalcSetting;
166 import jalview.ws.params.WsParamSetI;
167 import jalview.xml.binding.jalview.AlcodonFrame;
168 import jalview.xml.binding.jalview.AlcodonFrame.AlcodMap;
169 import jalview.xml.binding.jalview.Annotation;
170 import jalview.xml.binding.jalview.Annotation.ThresholdLine;
171 import jalview.xml.binding.jalview.AnnotationColourScheme;
172 import jalview.xml.binding.jalview.AnnotationElement;
173 import jalview.xml.binding.jalview.DoubleMatrix;
174 import jalview.xml.binding.jalview.DoubleVector;
175 import jalview.xml.binding.jalview.Feature;
176 import jalview.xml.binding.jalview.Feature.OtherData;
177 import jalview.xml.binding.jalview.FeatureMatcherSet.CompoundMatcher;
178 import jalview.xml.binding.jalview.FilterBy;
179 import jalview.xml.binding.jalview.JalviewModel;
180 import jalview.xml.binding.jalview.JalviewModel.FeatureSettings;
181 import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Group;
182 import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Setting;
183 import jalview.xml.binding.jalview.JalviewModel.JGroup;
184 import jalview.xml.binding.jalview.JalviewModel.JSeq;
185 import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids;
186 import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids.StructureState;
187 import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer;
188 import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer.SecondaryStructure;
189 import jalview.xml.binding.jalview.JalviewModel.PcaViewer;
190 import jalview.xml.binding.jalview.JalviewModel.PcaViewer.Axis;
191 import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMax;
192 import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMin;
193 import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SequencePoint;
194 import jalview.xml.binding.jalview.JalviewModel.Tree;
195 import jalview.xml.binding.jalview.JalviewModel.UserColours;
196 import jalview.xml.binding.jalview.JalviewModel.Viewport;
197 import jalview.xml.binding.jalview.JalviewModel.Viewport.CalcIdParam;
198 import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns;
199 import jalview.xml.binding.jalview.JalviewModel.Viewport.Overview;
200 import jalview.xml.binding.jalview.JalviewUserColours;
201 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
202 import jalview.xml.binding.jalview.MapListType;
203 import jalview.xml.binding.jalview.MapListType.MapListFrom;
204 import jalview.xml.binding.jalview.MapListType.MapListTo;
205 import jalview.xml.binding.jalview.MapOnAMatrixType;
206 import jalview.xml.binding.jalview.Mapping;
207 import jalview.xml.binding.jalview.MatrixType;
208 import jalview.xml.binding.jalview.NoValueColour;
209 import jalview.xml.binding.jalview.ObjectFactory;
210 import jalview.xml.binding.jalview.PcaDataType;
211 import jalview.xml.binding.jalview.Pdbentry.Property;
212 import jalview.xml.binding.jalview.Sequence;
213 import jalview.xml.binding.jalview.Sequence.DBRef;
214 import jalview.xml.binding.jalview.SequenceSet;
215 import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties;
216 import jalview.xml.binding.jalview.ThresholdType;
217 import jalview.xml.binding.jalview.VAMSAS;
220 * Write out the current jalview desktop state as a Jalview XML stream.
222 * Note: the vamsas objects referred to here are primitive versions of the
223 * VAMSAS project schema elements - they are not the same and most likely never
227 * @version $Revision: 1.134 $
229 public class Jalview2XML
232 // BH 2018 we add the .jvp binary extension to J2S so that
233 // it will declare that binary when we do the file save from the browser
237 Platform.addJ2SBinaryType(".jvp?");
240 private static final String VIEWER_PREFIX = "viewer_";
242 private static final String RNA_PREFIX = "rna_";
244 private static final String UTF_8 = "UTF-8";
247 * used in decision if quit confirmation should be issued
249 private static boolean stateSavedUpToDate = false;
252 * prefix for recovering datasets for alignments with multiple views where
253 * non-existent dataset IDs were written for some views
255 private static final String UNIQSEQSETID = "uniqueSeqSetId.";
257 // use this with nextCounter() to make unique names for entities
258 private int counter = 0;
261 * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
262 * of sequence objects are created.
264 IdentityHashMap<SequenceI, String> seqsToIds = null;
267 * jalview XML Sequence ID to jalview sequence object reference (both dataset
268 * and alignment sequences. Populated as XML reps of sequence objects are
271 Map<String, SequenceI> seqRefIds = null;
273 Map<String, SequenceI> incompleteSeqs = null;
275 List<forwardRef> frefedSequence = null;
277 boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
280 * Map of reconstructed AlignFrame objects that appear to have come from
281 * SplitFrame objects (have a dna/protein complement view).
283 private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<>();
286 * Map from displayed rna structure models to their saved session state jar
289 private Map<RnaModel, String> rnaSessions = new HashMap<>();
292 * map from contact matrices to their XML ids
294 private Map<ContactMatrixI, String> contactMatrices = new HashMap<>();
296 private Map<String, ContactMatrixI> contactMatrixRefs = new HashMap<>();
298 private List<jalview.xml.binding.jalview.MatrixType> xmlMatrices = new ArrayList<>();
301 * A helper method for safely using the value of an optional attribute that
302 * may be null if not present in the XML. Answers the boolean value, or false
308 public static boolean safeBoolean(Boolean b)
310 return b == null ? false : b.booleanValue();
314 * A helper method for safely using the value of an optional attribute that
315 * may be null if not present in the XML. Answers the integer value, or zero
321 public static int safeInt(Integer i)
323 return i == null ? 0 : i.intValue();
327 * A helper method for safely using the value of an optional attribute that
328 * may be null if not present in the XML. Answers the float value, or zero if
334 public static float safeFloat(Float f)
336 return f == null ? 0f : f.floatValue();
340 * create/return unique hash string for sq
343 * @return new or existing unique string for sq
345 String seqHash(SequenceI sq)
347 if (seqsToIds == null)
351 if (seqsToIds.containsKey(sq))
353 return seqsToIds.get(sq);
357 // create sequential key
358 String key = "sq" + (seqsToIds.size() + 1);
359 key = makeHashCode(sq, key); // check we don't have an external reference
361 seqsToIds.put(sq, key);
368 if (seqsToIds == null)
370 seqsToIds = new IdentityHashMap<>();
372 if (seqRefIds == null)
374 seqRefIds = new HashMap<>();
376 if (incompleteSeqs == null)
378 incompleteSeqs = new HashMap<>();
380 if (frefedSequence == null)
382 frefedSequence = new ArrayList<>();
390 public Jalview2XML(boolean raiseGUI)
392 this.raiseGUI = raiseGUI;
396 * base class for resolving forward references to an as-yet unmarshalled
397 * object referenced by already unmarshalled objects
402 abstract class forwardRef
408 public forwardRef(String _sref, String type)
414 public String getSref()
419 public abstract boolean isResolvable();
422 * @return true if the forward reference was fully resolved
424 abstract boolean resolve();
427 public String toString()
429 return type + " reference to " + sref;
434 * resolve forward references to sequences by their ID
438 abstract class SeqFref extends forwardRef
440 public SeqFref(String _sref, String type)
445 public SequenceI getSrefSeq()
447 return seqRefIds.get(sref);
450 public boolean isResolvable()
452 return seqRefIds.get(sref) != null;
455 public SequenceI getSrefDatasetSeq()
457 SequenceI sq = seqRefIds.get(sref);
460 while (sq.getDatasetSequence() != null)
462 sq = sq.getDatasetSequence();
470 * create forward reference for a mapping
476 public SeqFref newMappingRef(final String sref,
477 final jalview.datamodel.Mapping _jmap)
479 SeqFref fref = new SeqFref(sref, "Mapping")
481 public jalview.datamodel.Mapping jmap = _jmap;
486 SequenceI seq = getSrefDatasetSeq();
498 public SeqFref newAlcodMapRef(final String sref,
499 final AlignedCodonFrame _cf,
500 final jalview.datamodel.Mapping _jmap)
503 SeqFref fref = new SeqFref(sref, "Codon Frame")
505 AlignedCodonFrame cf = _cf;
507 public jalview.datamodel.Mapping mp = _jmap;
510 public boolean isResolvable()
512 return super.isResolvable() && mp.getTo() != null;
518 SequenceI seq = getSrefDatasetSeq();
523 cf.addMap(seq, mp.getTo(), mp.getMap());
530 public forwardRef newMatrixFref(final String matRef,
531 final jalview.util.MapList mapping, final AlignmentAnnotation jaa)
533 forwardRef fref = new forwardRef(matRef,
534 "Matrix Reference for sequence and annotation")
540 ContactMatrixI cm = contactMatrixRefs.get(matRef);
541 PAEContactMatrix newpae = new PAEContactMatrix(jaa.sequenceRef,
544 jaa.sequenceRef.addContactListFor(jaa, newpae);
549 public boolean isResolvable()
551 return (contactMatrixRefs.get(matRef) != null);
557 public void resolveFrefedSequences()
559 Iterator<forwardRef> nextFref = frefedSequence.iterator();
560 int toresolve = frefedSequence.size();
561 int unresolved = 0, failedtoresolve = 0;
562 while (nextFref.hasNext())
564 forwardRef ref = nextFref.next();
565 if (ref.isResolvable())
577 } catch (Exception x)
579 jalview.bin.Console.errPrintln(
580 "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
593 jalview.bin.Console.errPrintln("Jalview Project Import: There were "
595 + " forward references left unresolved on the stack.");
597 if (failedtoresolve > 0)
599 jalview.bin.Console.errPrintln("SERIOUS! " + failedtoresolve
600 + " resolvable forward references failed to resolve.");
602 if (incompleteSeqs != null && incompleteSeqs.size() > 0)
604 jalview.bin.Console.errPrintln(
605 "Jalview Project Import: There are " + incompleteSeqs.size()
606 + " sequences which may have incomplete metadata.");
607 if (incompleteSeqs.size() < 10)
609 for (SequenceI s : incompleteSeqs.values())
611 jalview.bin.Console.errPrintln(s.toString());
616 jalview.bin.Console.errPrintln(
617 "Too many to report. Skipping output of incomplete sequences.");
623 * This maintains a map of viewports, the key being the seqSetId. Important to
624 * set historyItem and redoList for multiple views
626 Map<String, AlignViewport> viewportsAdded = new HashMap<>();
628 Map<String, AlignmentAnnotation> annotationIds = new HashMap<>();
630 String uniqueSetSuffix = "";
633 * List of pdbfiles added to Jar
635 List<String> pdbfiles = null;
637 // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
638 public void saveState(File statefile)
640 FileOutputStream fos = null;
645 fos = new FileOutputStream(statefile);
647 JarOutputStream jout = new JarOutputStream(fos);
651 } catch (Exception e)
653 Console.error("Couln't write Jalview state to " + statefile, e);
654 // TODO: inform user of the problem - they need to know if their data was
656 if (errorMessage == null)
658 errorMessage = "Did't write Jalview Archive to output file '"
659 + statefile + "' - See console error log for details";
663 errorMessage += "(Didn't write Jalview Archive to output file '"
674 } catch (IOException e)
684 * Writes a jalview project archive to the given Jar output stream.
688 public void saveState(JarOutputStream jout)
690 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
692 setStateSavedUpToDate(true);
694 if (Cache.getDefault("DEBUG_DELAY_SAVE", false))
696 int n = debugDelaySave;
700 Console.debug("***** debugging save sleep " + i + "/" + n);
704 } catch (InterruptedException e)
706 // TODO Auto-generated catch block
717 saveAllFrames(Arrays.asList(frames), jout);
721 * core method for storing state for a set of AlignFrames.
724 * - frames involving all data to be exported (including containing
727 * - project output stream
729 private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
731 Hashtable<String, AlignFrame> dsses = new Hashtable<>();
734 * ensure cached data is clear before starting
736 // todo tidy up seqRefIds, seqsToIds initialisation / reset
738 splitFrameCandidates.clear();
743 // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
744 // //////////////////////////////////////////////////
746 List<String> shortNames = new ArrayList<>();
747 List<String> viewIds = new ArrayList<>();
750 for (int i = frames.size() - 1; i > -1; i--)
752 AlignFrame af = frames.get(i);
754 if (skipList != null && skipList
755 .containsKey(af.getViewport().getSequenceSetId()))
760 String shortName = makeFilename(af, shortNames);
762 int apSize = af.getAlignPanels().size();
764 for (int ap = 0; ap < apSize; ap++)
766 AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
768 String fileName = apSize == 1 ? shortName : ap + shortName;
769 if (!fileName.endsWith(".xml"))
771 fileName = fileName + ".xml";
774 saveState(apanel, fileName, jout, viewIds);
776 String dssid = getDatasetIdRef(
777 af.getViewport().getAlignment().getDataset());
778 if (!dsses.containsKey(dssid))
780 dsses.put(dssid, af);
785 writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
791 } catch (Exception foo)
795 } catch (Exception ex)
797 // TODO: inform user of the problem - they need to know if their data was
799 if (errorMessage == null)
801 errorMessage = "Couldn't write Jalview Archive - see error output for details";
803 ex.printStackTrace();
808 * Generates a distinct file name, based on the title of the AlignFrame, by
809 * appending _n for increasing n until an unused name is generated. The new
810 * name (without its extension) is added to the list.
814 * @return the generated name, with .xml extension
816 protected String makeFilename(AlignFrame af, List<String> namesUsed)
818 String shortName = af.getTitle();
820 if (shortName.indexOf(File.separatorChar) > -1)
822 shortName = shortName
823 .substring(shortName.lastIndexOf(File.separatorChar) + 1);
828 while (namesUsed.contains(shortName))
830 if (shortName.endsWith("_" + (count - 1)))
832 shortName = shortName.substring(0, shortName.lastIndexOf("_"));
835 shortName = shortName.concat("_" + count);
839 namesUsed.add(shortName);
841 if (!shortName.endsWith(".xml"))
843 shortName = shortName + ".xml";
848 // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
849 public boolean saveAlignment(AlignFrame af, String jarFile,
854 // create backupfiles object and get new temp filename destination
855 boolean doBackup = BackupFiles.getEnabled();
856 BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
857 FileOutputStream fos = new FileOutputStream(
858 doBackup ? backupfiles.getTempFilePath() : jarFile);
860 if (Cache.getDefault("DEBUG_DELAY_SAVE", false))
862 int n = debugDelaySave;
866 Console.debug("***** debugging save sleep " + i + "/" + n);
870 } catch (InterruptedException e)
872 // TODO Auto-generated catch block
879 JarOutputStream jout = new JarOutputStream(fos);
880 List<AlignFrame> frames = new ArrayList<>();
882 // resolve splitframes
883 if (af.getViewport().getCodingComplement() != null)
885 frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
891 saveAllFrames(frames, jout);
895 } catch (Exception foo)
899 boolean success = true;
903 backupfiles.setWriteSuccess(success);
904 success = backupfiles.rollBackupsAndRenameTempFile();
908 } catch (Exception ex)
910 errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
911 ex.printStackTrace();
916 private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
917 String fileName, JarOutputStream jout)
920 for (String dssids : dsses.keySet())
922 AlignFrame _af = dsses.get(dssids);
923 String jfileName = fileName + " Dataset for " + _af.getTitle();
924 if (!jfileName.endsWith(".xml"))
926 jfileName = jfileName + ".xml";
928 saveState(_af.alignPanel, jfileName, true, jout, null);
933 * create a JalviewModel from an alignment view and marshall it to a
937 * panel to create jalview model for
939 * name of alignment panel written to output stream
946 public JalviewModel saveState(AlignmentPanel ap, String fileName,
947 JarOutputStream jout, List<String> viewIds)
949 return saveState(ap, fileName, false, jout, viewIds);
953 * create a JalviewModel from an alignment view and marshall it to a
957 * panel to create jalview model for
959 * name of alignment panel written to output stream
961 * when true, only write the dataset for the alignment, not the data
962 * associated with the view.
968 public JalviewModel saveState(AlignmentPanel ap, String fileName,
969 boolean storeDS, JarOutputStream jout, List<String> viewIds)
973 viewIds = new ArrayList<>();
978 List<UserColourScheme> userColours = new ArrayList<>();
980 AlignViewport av = ap.av;
981 ViewportRanges vpRanges = av.getRanges();
983 final ObjectFactory objectFactory = new ObjectFactory();
984 JalviewModel object = objectFactory.createJalviewModel();
985 object.setVamsasModel(new VAMSAS());
987 // object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
990 GregorianCalendar c = new GregorianCalendar();
991 DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
992 XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar);
993 object.setCreationDate(now);
994 } catch (DatatypeConfigurationException e)
996 jalview.bin.Console.errPrintln("error writing date: " + e.toString());
998 object.setVersion(Cache.getDefault("VERSION", "Development Build"));
1001 * rjal is full height alignment, jal is actual alignment with full metadata
1002 * but excludes hidden sequences.
1004 jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
1006 if (av.hasHiddenRows())
1008 rjal = jal.getHiddenSequences().getFullAlignment();
1011 SequenceSet vamsasSet = new SequenceSet();
1013 // JalviewModelSequence jms = new JalviewModelSequence();
1015 vamsasSet.setGapChar(jal.getGapCharacter() + "");
1017 if (jal.getDataset() != null)
1019 // dataset id is the dataset's hashcode
1020 vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
1023 // switch jal and the dataset
1024 jal = jal.getDataset();
1028 if (jal.getProperties() != null)
1030 Enumeration en = jal.getProperties().keys();
1031 while (en.hasMoreElements())
1033 String key = en.nextElement().toString();
1034 SequenceSetProperties ssp = new SequenceSetProperties();
1036 ssp.setValue(jal.getProperties().get(key).toString());
1037 // vamsasSet.addSequenceSetProperties(ssp);
1038 vamsasSet.getSequenceSetProperties().add(ssp);
1043 Set<String> calcIdSet = new HashSet<>();
1044 // record the set of vamsas sequence XML POJO we create.
1045 HashMap<String, Sequence> vamsasSetIds = new HashMap<>();
1047 for (final SequenceI jds : rjal.getSequences())
1049 final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
1050 : jds.getDatasetSequence();
1051 String id = seqHash(jds);
1052 if (vamsasSetIds.get(id) == null)
1054 if (seqRefIds.get(id) != null && !storeDS)
1056 // This happens for two reasons: 1. multiple views are being
1058 // 2. the hashCode has collided with another sequence's code. This
1060 // HAPPEN! (PF00072.15.stk does this)
1061 // JBPNote: Uncomment to debug writing out of files that do not read
1062 // back in due to ArrayOutOfBoundExceptions.
1063 // jalview.bin.Console.errPrintln("vamsasSeq backref: "+id+"");
1064 // jalview.bin.Console.errPrintln(jds.getName()+"
1065 // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
1066 // jalview.bin.Console.errPrintln("Hashcode: "+seqHash(jds));
1067 // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
1068 // jalview.bin.Console.errPrintln(rsq.getName()+"
1069 // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
1070 // jalview.bin.Console.errPrintln("Hashcode: "+seqHash(rsq));
1074 vamsasSeq = createVamsasSequence(id, jds);
1075 // vamsasSet.addSequence(vamsasSeq);
1076 vamsasSet.getSequence().add(vamsasSeq);
1077 vamsasSetIds.put(id, vamsasSeq);
1078 seqRefIds.put(id, jds);
1082 jseq.setStart(jds.getStart());
1083 jseq.setEnd(jds.getEnd());
1084 jseq.setColour(av.getSequenceColour(jds).getRGB());
1086 jseq.setId(id); // jseq id should be a string not a number
1089 // Store any sequences this sequence represents
1090 if (av.hasHiddenRows())
1092 // use rjal, contains the full height alignment
1094 av.getAlignment().getHiddenSequences().isHidden(jds));
1096 if (av.isHiddenRepSequence(jds))
1098 jalview.datamodel.SequenceI[] reps = av
1099 .getRepresentedSequences(jds).getSequencesInOrder(rjal);
1101 for (int h = 0; h < reps.length; h++)
1105 // jseq.addHiddenSequences(rjal.findIndex(reps[h]));
1106 jseq.getHiddenSequences().add(rjal.findIndex(reps[h]));
1111 // mark sequence as reference - if it is the reference for this view
1112 if (jal.hasSeqrep())
1114 jseq.setViewreference(jds == jal.getSeqrep());
1118 // TODO: omit sequence features from each alignment view's XML dump if we
1119 // are storing dataset
1120 List<SequenceFeature> sfs = jds.getSequenceFeatures();
1121 for (SequenceFeature sf : sfs)
1123 // Features features = new Features();
1124 Feature features = new Feature();
1126 features.setBegin(sf.getBegin());
1127 features.setEnd(sf.getEnd());
1128 features.setDescription(sf.getDescription());
1129 features.setType(sf.getType());
1130 features.setFeatureGroup(sf.getFeatureGroup());
1131 features.setScore(sf.getScore());
1132 if (sf.links != null)
1134 for (int l = 0; l < sf.links.size(); l++)
1136 OtherData keyValue = new OtherData();
1137 keyValue.setKey("LINK_" + l);
1138 keyValue.setValue(sf.links.elementAt(l).toString());
1139 // features.addOtherData(keyValue);
1140 features.getOtherData().add(keyValue);
1143 if (sf.otherDetails != null)
1146 * save feature attributes, which may be simple strings or
1147 * map valued (have sub-attributes)
1149 for (Entry<String, Object> entry : sf.otherDetails.entrySet())
1151 String key = entry.getKey();
1152 Object value = entry.getValue();
1153 if (value instanceof Map<?, ?>)
1155 for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
1158 OtherData otherData = new OtherData();
1159 otherData.setKey(key);
1160 otherData.setKey2(subAttribute.getKey());
1161 otherData.setValue(subAttribute.getValue().toString());
1162 // features.addOtherData(otherData);
1163 features.getOtherData().add(otherData);
1168 OtherData otherData = new OtherData();
1169 otherData.setKey(key);
1170 otherData.setValue(value.toString());
1171 // features.addOtherData(otherData);
1172 features.getOtherData().add(otherData);
1177 // jseq.addFeatures(features);
1178 jseq.getFeatures().add(features);
1181 if (jdatasq.getAllPDBEntries() != null)
1183 Enumeration<PDBEntry> en = jdatasq.getAllPDBEntries().elements();
1184 while (en.hasMoreElements())
1186 Pdbids pdb = new Pdbids();
1187 jalview.datamodel.PDBEntry entry = en.nextElement();
1189 String pdbId = entry.getId();
1191 pdb.setType(entry.getType());
1194 * Store any structure views associated with this sequence. This
1195 * section copes with duplicate entries in the project, so a dataset
1196 * only view *should* be coped with sensibly.
1198 // This must have been loaded, is it still visible?
1199 List<JalviewStructureDisplayI> viewFrames = new ArrayList<>();
1200 if (Desktop.desktop != null)
1202 JInternalFrame[] jifs = Desktop.desktop.getAllFrames();
1205 for (JInternalFrame jif : jifs)
1207 if (jif instanceof JalviewStructureDisplayI)
1209 viewFrames.add((JalviewStructureDisplayI) jif);
1214 else if (Jalview.isHeadlessMode()
1215 && Jalview.getInstance().getCommands() != null)
1218 StructureViewerBase.getAllStructureViewerBases());
1221 String matchedFile = null;
1222 for (JalviewStructureDisplayI viewFrame : viewFrames)
1224 matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds,
1225 matchedFile, viewFrame);
1227 * Only store each structure viewer's state once in the project
1228 * jar. First time through only (storeDS==false)
1230 String viewId = viewFrame.getViewId();
1231 String viewerType = viewFrame.getViewerType().toString();
1232 if (!storeDS && !viewIds.contains(viewId))
1234 viewIds.add(viewId);
1235 File viewerState = viewFrame.saveSession();
1236 if (viewerState != null)
1238 copyFileToJar(jout, viewerState.getPath(),
1239 getViewerJarEntryName(viewId), viewerType);
1244 "Failed to save viewer state for " + viewerType);
1249 if (matchedFile != null || entry.getFile() != null)
1251 if (entry.getFile() != null)
1254 matchedFile = entry.getFile();
1256 pdb.setFile(matchedFile); // entry.getFile());
1257 if (pdbfiles == null)
1259 pdbfiles = new ArrayList<>();
1262 if (!pdbfiles.contains(pdbId))
1264 pdbfiles.add(pdbId);
1265 copyFileToJar(jout, matchedFile, pdbId, pdbId);
1269 Enumeration<String> props = entry.getProperties();
1270 if (props.hasMoreElements())
1272 // PdbentryItem item = new PdbentryItem();
1273 while (props.hasMoreElements())
1275 Property prop = new Property();
1276 String key = props.nextElement();
1278 prop.setValue(entry.getProperty(key).toString());
1279 // item.addProperty(prop);
1280 pdb.getProperty().add(prop);
1282 // pdb.addPdbentryItem(item);
1285 // jseq.addPdbids(pdb);
1286 jseq.getPdbids().add(pdb);
1290 saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS);
1292 // jms.addJSeq(jseq);
1293 object.getJSeq().add(jseq);
1296 if (!storeDS && av.hasHiddenRows())
1298 jal = av.getAlignment();
1302 if (storeDS && jal.getCodonFrames() != null)
1304 List<AlignedCodonFrame> jac = jal.getCodonFrames();
1305 for (AlignedCodonFrame acf : jac)
1307 AlcodonFrame alc = new AlcodonFrame();
1308 if (acf.getProtMappings() != null
1309 && acf.getProtMappings().length > 0)
1311 boolean hasMap = false;
1312 SequenceI[] dnas = acf.getdnaSeqs();
1313 jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
1314 for (int m = 0; m < pmaps.length; m++)
1316 AlcodMap alcmap = new AlcodMap();
1317 alcmap.setDnasq(seqHash(dnas[m]));
1319 createVamsasMapping(pmaps[m], dnas[m], null, false));
1320 // alc.addAlcodMap(alcmap);
1321 alc.getAlcodMap().add(alcmap);
1326 // vamsasSet.addAlcodonFrame(alc);
1327 vamsasSet.getAlcodonFrame().add(alc);
1330 // TODO: delete this ? dead code from 2.8.3->2.9 ?
1332 // AlcodonFrame alc = new AlcodonFrame();
1333 // vamsasSet.addAlcodonFrame(alc);
1334 // for (int p = 0; p < acf.aaWidth; p++)
1336 // Alcodon cmap = new Alcodon();
1337 // if (acf.codons[p] != null)
1339 // // Null codons indicate a gapped column in the translated peptide
1341 // cmap.setPos1(acf.codons[p][0]);
1342 // cmap.setPos2(acf.codons[p][1]);
1343 // cmap.setPos3(acf.codons[p][2]);
1345 // alc.addAlcodon(cmap);
1347 // if (acf.getProtMappings() != null
1348 // && acf.getProtMappings().length > 0)
1350 // SequenceI[] dnas = acf.getdnaSeqs();
1351 // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
1352 // for (int m = 0; m < pmaps.length; m++)
1354 // AlcodMap alcmap = new AlcodMap();
1355 // alcmap.setDnasq(seqHash(dnas[m]));
1356 // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
1358 // alc.addAlcodMap(alcmap);
1365 // /////////////////////////////////
1366 if (!storeDS && av.getCurrentTree() != null)
1368 // FIND ANY ASSOCIATED TREES
1369 // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
1370 if (Desktop.desktop != null)
1372 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1374 for (int t = 0; t < frames.length; t++)
1376 if (frames[t] instanceof TreePanel)
1378 TreePanel tp = (TreePanel) frames[t];
1380 if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
1382 JalviewModel.Tree tree = new JalviewModel.Tree();
1383 tree.setTitle(tp.getTitle());
1384 tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
1385 tree.setNewick(tp.getTree().print());
1386 tree.setThreshold(tp.getTreeCanvas().getThreshold());
1388 tree.setFitToWindow(tp.fitToWindow.getState());
1389 tree.setFontName(tp.getTreeFont().getName());
1390 tree.setFontSize(tp.getTreeFont().getSize());
1391 tree.setFontStyle(tp.getTreeFont().getStyle());
1392 tree.setMarkUnlinked(tp.placeholdersMenu.getState());
1394 tree.setShowBootstrap(tp.bootstrapMenu.getState());
1395 tree.setShowDistances(tp.distanceMenu.getState());
1397 tree.setHeight(tp.getHeight());
1398 tree.setWidth(tp.getWidth());
1399 tree.setXpos(tp.getX());
1400 tree.setYpos(tp.getY());
1401 tree.setId(makeHashCode(tp, null));
1402 tree.setLinkToAllViews(
1403 tp.getTreeCanvas().isApplyToAllViews());
1406 if (tp.isColumnWise())
1408 tree.setColumnWise(true);
1409 String annId = tp.getAssocAnnotation().annotationId;
1410 tree.setColumnReference(annId);
1412 // jms.addTree(tree);
1413 object.getTree().add(tree);
1423 if (!storeDS && Desktop.desktop != null)
1425 for (JInternalFrame frame : Desktop.desktop.getAllFrames())
1427 if (frame instanceof PCAPanel)
1429 PCAPanel panel = (PCAPanel) frame;
1430 if (panel.getAlignViewport().getAlignment() == jal)
1432 savePCA(panel, object);
1440 * store forward refs from an annotationRow to any groups
1442 IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<>();
1445 for (SequenceI sq : jal.getSequences())
1447 // Store annotation on dataset sequences only
1448 AlignmentAnnotation[] aa = sq.getAnnotation();
1449 if (aa != null && aa.length > 0)
1451 storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
1458 if (jal.getAlignmentAnnotation() != null)
1460 // Store the annotation shown on the alignment.
1461 AlignmentAnnotation[] aa = jal.getAlignmentAnnotation();
1462 storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
1467 if (jal.getGroups() != null)
1469 JGroup[] groups = new JGroup[jal.getGroups().size()];
1471 for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
1473 JGroup jGroup = new JGroup();
1474 groups[++i] = jGroup;
1476 jGroup.setStart(sg.getStartRes());
1477 jGroup.setEnd(sg.getEndRes());
1478 jGroup.setName(sg.getName());
1479 if (groupRefs.containsKey(sg))
1481 // group has references so set its ID field
1482 jGroup.setId(groupRefs.get(sg));
1484 ColourSchemeI colourScheme = sg.getColourScheme();
1485 if (colourScheme != null)
1487 ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
1488 if (groupColourScheme.conservationApplied())
1490 jGroup.setConsThreshold(groupColourScheme.getConservationInc());
1492 if (colourScheme instanceof jalview.schemes.UserColourScheme)
1494 jGroup.setColour(setUserColourScheme(colourScheme,
1495 userColours, object));
1499 jGroup.setColour(colourScheme.getSchemeName());
1502 else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
1504 jGroup.setColour("AnnotationColourGradient");
1505 jGroup.setAnnotationColours(constructAnnotationColours(
1506 (jalview.schemes.AnnotationColourGradient) colourScheme,
1507 userColours, object));
1509 else if (colourScheme instanceof jalview.schemes.UserColourScheme)
1512 setUserColourScheme(colourScheme, userColours, object));
1516 jGroup.setColour(colourScheme.getSchemeName());
1519 jGroup.setPidThreshold(groupColourScheme.getThreshold());
1522 jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
1523 jGroup.setDisplayBoxes(sg.getDisplayBoxes());
1524 jGroup.setDisplayText(sg.getDisplayText());
1525 jGroup.setColourText(sg.getColourText());
1526 jGroup.setTextCol1(sg.textColour.getRGB());
1527 jGroup.setTextCol2(sg.textColour2.getRGB());
1528 jGroup.setTextColThreshold(sg.thresholdTextColour);
1529 jGroup.setShowUnconserved(sg.getShowNonconserved());
1530 jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
1531 jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram());
1532 jGroup.setShowSequenceLogo(sg.isShowSequenceLogo());
1533 jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
1534 for (SequenceI seq : sg.getSequences())
1536 // jGroup.addSeq(seqHash(seq));
1537 jGroup.getSeq().add(seqHash(seq));
1541 // jms.setJGroup(groups);
1543 for (JGroup grp : groups)
1545 object.getJGroup().add(grp);
1550 // /////////SAVE VIEWPORT
1551 Viewport view = new Viewport();
1552 view.setTitle(ap.alignFrame.getTitle());
1553 view.setSequenceSetId(
1554 makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
1555 view.setId(av.getViewId());
1556 if (av.getCodingComplement() != null)
1558 view.setComplementId(av.getCodingComplement().getViewId());
1560 view.setViewName(av.getViewName());
1561 view.setGatheredViews(av.isGatherViewsHere());
1563 Rectangle size = ap.av.getExplodedGeometry();
1564 Rectangle position = size;
1567 size = ap.alignFrame.getBounds();
1568 if (av.getCodingComplement() != null)
1570 position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
1578 view.setXpos(position.x);
1579 view.setYpos(position.y);
1581 view.setWidth(size.width);
1582 view.setHeight(size.height);
1584 view.setStartRes(vpRanges.getStartRes());
1585 view.setStartSeq(vpRanges.getStartSeq());
1587 OverviewPanel ov = ap.getOverviewPanel();
1590 Overview overview = new Overview();
1591 overview.setTitle(ov.getTitle());
1592 Rectangle bounds = ov.getFrameBounds();
1593 overview.setXpos(bounds.x);
1594 overview.setYpos(bounds.y);
1595 overview.setWidth(bounds.width);
1596 overview.setHeight(bounds.height);
1597 overview.setShowHidden(ov.isShowHiddenRegions());
1598 overview.setGapColour(ov.getCanvas().getGapColour().getRGB());
1599 overview.setResidueColour(
1600 ov.getCanvas().getResidueColour().getRGB());
1601 overview.setHiddenColour(ov.getCanvas().getHiddenColour().getRGB());
1602 view.setOverview(overview);
1604 if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
1606 view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
1607 userColours, object));
1610 .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
1612 AnnotationColourScheme ac = constructAnnotationColours(
1613 (jalview.schemes.AnnotationColourGradient) av
1614 .getGlobalColourScheme(),
1615 userColours, object);
1617 view.setAnnotationColours(ac);
1618 view.setBgColour("AnnotationColourGradient");
1622 view.setBgColour(ColourSchemeProperty
1623 .getColourName(av.getGlobalColourScheme()));
1626 ResidueShaderI vcs = av.getResidueShading();
1627 ColourSchemeI cs = av.getGlobalColourScheme();
1631 if (vcs.conservationApplied())
1633 view.setConsThreshold(vcs.getConservationInc());
1634 if (cs instanceof jalview.schemes.UserColourScheme)
1636 view.setBgColour(setUserColourScheme(cs, userColours, object));
1639 view.setPidThreshold(vcs.getThreshold());
1642 view.setConservationSelected(av.getConservationSelected());
1643 view.setPidSelected(av.getAbovePIDThreshold());
1644 view.setCharHeight(av.getCharHeight());
1645 view.setCharWidth(av.getCharWidth());
1646 final Font font = av.getFont();
1647 view.setFontName(font.getName());
1648 view.setFontSize(font.getSize());
1649 view.setFontStyle(font.getStyle());
1650 view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
1651 view.setRenderGaps(av.isRenderGaps());
1652 view.setShowAnnotation(av.isShowAnnotation());
1653 view.setShowBoxes(av.getShowBoxes());
1654 view.setShowColourText(av.getColourText());
1655 view.setShowFullId(av.getShowJVSuffix());
1656 view.setRightAlignIds(av.isRightAlignIds());
1657 view.setIdWidth(av.getIdWidth());
1658 view.setIdWidthManuallyAdjusted(
1659 ap.getIdPanel().getIdCanvas().isManuallyAdjusted());
1661 view.setShowSequenceFeatures(av.isShowSequenceFeatures());
1662 view.setShowText(av.getShowText());
1663 view.setShowUnconserved(av.getShowUnconserved());
1664 view.setWrapAlignment(av.getWrapAlignment());
1665 view.setTextCol1(av.getTextColour().getRGB());
1666 view.setTextCol2(av.getTextColour2().getRGB());
1667 view.setTextColThreshold(av.getThresholdTextColour());
1668 view.setShowConsensusHistogram(av.isShowConsensusHistogram());
1669 view.setShowSequenceLogo(av.isShowSequenceLogo());
1670 view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
1671 view.setShowGroupConsensus(av.isShowGroupConsensus());
1672 view.setShowGroupConservation(av.isShowGroupConservation());
1673 view.setShowNPfeatureTooltip(av.isShowNPFeats());
1674 view.setShowDbRefTooltip(av.isShowDBRefs());
1675 view.setFollowHighlight(av.isFollowHighlight());
1676 view.setFollowSelection(av.followSelection);
1677 view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
1678 view.setShowComplementFeatures(av.isShowComplementFeatures());
1679 view.setShowComplementFeaturesOnTop(
1680 av.isShowComplementFeaturesOnTop());
1681 if (av.getFeaturesDisplayed() != null)
1683 FeatureSettings fs = new FeatureSettings();
1685 FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
1686 .getFeatureRenderer();
1687 String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
1689 Vector<String> settingsAdded = new Vector<>();
1690 if (renderOrder != null)
1692 for (String featureType : renderOrder)
1694 FeatureSettings.Setting setting = new FeatureSettings.Setting();
1695 setting.setType(featureType);
1698 * save any filter for the feature type
1700 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1703 Iterator<FeatureMatcherI> filters = filter.getMatchers()
1705 FeatureMatcherI firstFilter = filters.next();
1706 setting.setMatcherSet(Jalview2XML.marshalFilter(firstFilter,
1707 filters, filter.isAnded()));
1711 * save colour scheme for the feature type
1713 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1714 if (!fcol.isSimpleColour())
1716 setting.setColour(fcol.getMaxColour().getRGB());
1717 setting.setMincolour(fcol.getMinColour().getRGB());
1718 setting.setMin(fcol.getMin());
1719 setting.setMax(fcol.getMax());
1720 setting.setColourByLabel(fcol.isColourByLabel());
1721 if (fcol.isColourByAttribute())
1723 String[] attName = fcol.getAttributeName();
1724 setting.getAttributeName().add(attName[0]);
1725 if (attName.length > 1)
1727 setting.getAttributeName().add(attName[1]);
1730 setting.setAutoScale(fcol.isAutoScaled());
1731 setting.setThreshold(fcol.getThreshold());
1732 Color noColour = fcol.getNoColour();
1733 if (noColour == null)
1735 setting.setNoValueColour(NoValueColour.NONE);
1737 else if (noColour.equals(fcol.getMaxColour()))
1739 setting.setNoValueColour(NoValueColour.MAX);
1743 setting.setNoValueColour(NoValueColour.MIN);
1745 // -1 = No threshold, 0 = Below, 1 = Above
1746 setting.setThreshstate(fcol.isAboveThreshold() ? 1
1747 : (fcol.isBelowThreshold() ? 0 : -1));
1751 setting.setColour(fcol.getColour().getRGB());
1755 av.getFeaturesDisplayed().isVisible(featureType));
1756 float rorder = fr.getOrder(featureType);
1759 setting.setOrder(rorder);
1761 /// fs.addSetting(setting);
1762 fs.getSetting().add(setting);
1763 settingsAdded.addElement(featureType);
1767 // is groups actually supposed to be a map here ?
1768 Iterator<String> en = fr.getFeatureGroups().iterator();
1769 Vector<String> groupsAdded = new Vector<>();
1770 while (en.hasNext())
1772 String grp = en.next();
1773 if (groupsAdded.contains(grp))
1777 Group g = new Group();
1779 g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
1782 fs.getGroup().add(g);
1783 groupsAdded.addElement(grp);
1785 // jms.setFeatureSettings(fs);
1786 object.setFeatureSettings(fs);
1789 if (av.hasHiddenColumns())
1791 jalview.datamodel.HiddenColumns hidden = av.getAlignment()
1792 .getHiddenColumns();
1796 "REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1800 Iterator<int[]> hiddenRegions = hidden.iterator();
1801 while (hiddenRegions.hasNext())
1803 int[] region = hiddenRegions.next();
1804 HiddenColumns hc = new HiddenColumns();
1805 hc.setStart(region[0]);
1806 hc.setEnd(region[1]);
1807 // view.addHiddenColumns(hc);
1808 view.getHiddenColumns().add(hc);
1812 if (calcIdSet.size() > 0)
1814 for (String calcId : calcIdSet)
1816 if (calcId.trim().length() > 0)
1818 CalcIdParam cidp = createCalcIdParam(calcId, av);
1819 // Some calcIds have no parameters.
1822 // view.addCalcIdParam(cidp);
1823 view.getCalcIdParam().add(cidp);
1829 // jms.addViewport(view);
1830 object.getViewport().add(view);
1835 // store matrices referenced by any views or annotation in this dataset
1836 if (xmlMatrices != null && xmlMatrices.size() > 0)
1839 "Adding " + xmlMatrices.size() + " matrices to dataset.");
1840 vamsasSet.getMatrix().addAll(xmlMatrices);
1841 xmlMatrices.clear();
1845 // object.setJalviewModelSequence(jms);
1846 // object.getVamsasModel().addSequenceSet(vamsasSet);
1847 object.getVamsasModel().getSequenceSet().add(vamsasSet);
1849 if (jout != null && fileName != null)
1851 // We may not want to write the object to disk,
1852 // eg we can copy the alignViewport to a new view object
1853 // using save and then load
1856 fileName = fileName.replace('\\', '/');
1857 jalview.bin.Console.outPrintln("Writing jar entry " + fileName);
1858 JarEntry entry = new JarEntry(fileName);
1859 jout.putNextEntry(entry);
1860 PrintWriter pout = new PrintWriter(
1861 new OutputStreamWriter(jout, UTF_8));
1862 JAXBContext jaxbContext = JAXBContext
1863 .newInstance(JalviewModel.class);
1864 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1866 // output pretty printed
1867 // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
1868 jaxbMarshaller.marshal(
1869 new ObjectFactory().createJalviewModel(object), pout);
1871 // jaxbMarshaller.marshal(object, pout);
1872 // marshaller.marshal(object);
1875 } catch (Exception ex)
1877 // TODO: raise error in GUI if marshalling failed.
1878 jalview.bin.Console.errPrintln("Error writing Jalview project");
1879 ex.printStackTrace();
1886 * Writes PCA viewer attributes and computed values to an XML model object and
1887 * adds it to the JalviewModel. Any exceptions are reported by logging.
1889 protected void savePCA(PCAPanel panel, JalviewModel object)
1893 PcaViewer viewer = new PcaViewer();
1894 viewer.setHeight(panel.getHeight());
1895 viewer.setWidth(panel.getWidth());
1896 viewer.setXpos(panel.getX());
1897 viewer.setYpos(panel.getY());
1898 viewer.setTitle(panel.getTitle());
1899 PCAModel pcaModel = panel.getPcaModel();
1900 viewer.setScoreModelName(pcaModel.getScoreModelName());
1901 viewer.setXDim(panel.getSelectedDimensionIndex(X));
1902 viewer.setYDim(panel.getSelectedDimensionIndex(Y));
1903 viewer.setZDim(panel.getSelectedDimensionIndex(Z));
1905 panel.getRotatableCanvas().getBackgroundColour().getRGB());
1906 viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
1907 float[] spMin = panel.getRotatableCanvas().getSeqMin();
1908 SeqPointMin spmin = new SeqPointMin();
1909 spmin.setXPos(spMin[0]);
1910 spmin.setYPos(spMin[1]);
1911 spmin.setZPos(spMin[2]);
1912 viewer.setSeqPointMin(spmin);
1913 float[] spMax = panel.getRotatableCanvas().getSeqMax();
1914 SeqPointMax spmax = new SeqPointMax();
1915 spmax.setXPos(spMax[0]);
1916 spmax.setYPos(spMax[1]);
1917 spmax.setZPos(spMax[2]);
1918 viewer.setSeqPointMax(spmax);
1919 viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
1920 viewer.setLinkToAllViews(
1921 panel.getRotatableCanvas().isApplyToAllViews());
1922 SimilarityParamsI sp = pcaModel.getSimilarityParameters();
1923 viewer.setIncludeGaps(sp.includeGaps());
1924 viewer.setMatchGaps(sp.matchGaps());
1925 viewer.setIncludeGappedColumns(sp.includeGappedColumns());
1926 viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
1929 * sequence points on display
1931 for (jalview.datamodel.SequencePoint spt : pcaModel
1932 .getSequencePoints())
1934 SequencePoint point = new SequencePoint();
1935 point.setSequenceRef(seqHash(spt.getSequence()));
1936 point.setXPos(spt.coord.x);
1937 point.setYPos(spt.coord.y);
1938 point.setZPos(spt.coord.z);
1939 viewer.getSequencePoint().add(point);
1943 * (end points of) axes on display
1945 for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
1948 Axis axis = new Axis();
1952 viewer.getAxis().add(axis);
1956 * raw PCA data (note we are not restoring PCA inputs here -
1957 * alignment view, score model, similarity parameters)
1959 PcaDataType data = new PcaDataType();
1960 viewer.setPcaData(data);
1961 PCA pca = pcaModel.getPcaData();
1963 DoubleMatrix pm = new DoubleMatrix();
1964 saveDoubleMatrix(pca.getPairwiseScores(), pm);
1965 data.setPairwiseMatrix(pm);
1967 DoubleMatrix tm = new DoubleMatrix();
1968 saveDoubleMatrix(pca.getTridiagonal(), tm);
1969 data.setTridiagonalMatrix(tm);
1971 DoubleMatrix eigenMatrix = new DoubleMatrix();
1972 data.setEigenMatrix(eigenMatrix);
1973 saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
1975 object.getPcaViewer().add(viewer);
1976 } catch (Throwable t)
1978 Console.error("Error saving PCA: " + t.getMessage());
1983 * Stores values from a matrix into an XML element, including (if present) the
1988 * @see #loadDoubleMatrix(DoubleMatrix)
1990 protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
1992 xmlMatrix.setRows(m.height());
1993 xmlMatrix.setColumns(m.width());
1994 for (int i = 0; i < m.height(); i++)
1996 DoubleVector row = new DoubleVector();
1997 for (int j = 0; j < m.width(); j++)
1999 row.getV().add(m.getValue(i, j));
2001 xmlMatrix.getRow().add(row);
2003 if (m.getD() != null)
2005 DoubleVector dVector = new DoubleVector();
2006 for (double d : m.getD())
2008 dVector.getV().add(d);
2010 xmlMatrix.setD(dVector);
2012 if (m.getE() != null)
2014 DoubleVector eVector = new DoubleVector();
2015 for (double e : m.getE())
2017 eVector.getV().add(e);
2019 xmlMatrix.setE(eVector);
2024 * Loads XML matrix data into a new Matrix object, including the D and/or E
2025 * vectors (if present)
2029 * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
2031 protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
2033 int rows = mData.getRows();
2034 double[][] vals = new double[rows][];
2036 for (int i = 0; i < rows; i++)
2038 List<Double> dVector = mData.getRow().get(i).getV();
2039 vals[i] = new double[dVector.size()];
2041 for (Double d : dVector)
2047 MatrixI m = new Matrix(vals);
2049 if (mData.getD() != null)
2051 List<Double> dVector = mData.getD().getV();
2052 double[] vec = new double[dVector.size()];
2054 for (Double d : dVector)
2060 if (mData.getE() != null)
2062 List<Double> dVector = mData.getE().getV();
2063 double[] vec = new double[dVector.size()];
2065 for (Double d : dVector)
2076 * Save any Varna viewers linked to this sequence. Writes an rnaViewer element
2077 * for each viewer, with
2079 * <li>viewer geometry (position, size, split pane divider location)</li>
2080 * <li>index of the selected structure in the viewer (currently shows gapped
2082 * <li>the id of the annotation holding RNA secondary structure</li>
2083 * <li>(currently only one SS is shown per viewer, may be more in future)</li>
2085 * Varna viewer state is also written out (in native Varna XML) to separate
2086 * project jar entries. A separate entry is written for each RNA structure
2087 * displayed, with the naming convention
2089 * <li>rna_viewId_sequenceId_annotationId_[gapped|trimmed]</li>
2097 * @param storeDataset
2099 protected void saveRnaViewers(JarOutputStream jout, JSeq jseq,
2100 final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
2101 boolean storeDataset)
2103 if (Desktop.desktop == null)
2107 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2108 for (int f = frames.length - 1; f > -1; f--)
2110 if (frames[f] instanceof AppVarna)
2112 AppVarna varna = (AppVarna) frames[f];
2114 * link the sequence to every viewer that is showing it and is linked to
2115 * its alignment panel
2117 if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel())
2119 String viewId = varna.getViewId();
2120 RnaViewer rna = new RnaViewer();
2121 rna.setViewId(viewId);
2122 rna.setTitle(varna.getTitle());
2123 rna.setXpos(varna.getX());
2124 rna.setYpos(varna.getY());
2125 rna.setWidth(varna.getWidth());
2126 rna.setHeight(varna.getHeight());
2127 rna.setDividerLocation(varna.getDividerLocation());
2128 rna.setSelectedRna(varna.getSelectedIndex());
2129 // jseq.addRnaViewer(rna);
2130 jseq.getRnaViewer().add(rna);
2133 * Store each Varna panel's state once in the project per sequence.
2134 * First time through only (storeDataset==false)
2136 // boolean storeSessions = false;
2137 // String sequenceViewId = viewId + seqsToIds.get(jds);
2138 // if (!storeDataset && !viewIds.contains(sequenceViewId))
2140 // viewIds.add(sequenceViewId);
2141 // storeSessions = true;
2143 for (RnaModel model : varna.getModels())
2145 if (model.seq == jds)
2148 * VARNA saves each view (sequence or alignment secondary
2149 * structure, gapped or trimmed) as a separate XML file
2151 String jarEntryName = rnaSessions.get(model);
2152 if (jarEntryName == null)
2155 String varnaStateFile = varna.getStateInfo(model.rna);
2156 jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
2157 copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna");
2158 rnaSessions.put(model, jarEntryName);
2160 SecondaryStructure ss = new SecondaryStructure();
2161 String annotationId = varna.getAnnotation(jds).annotationId;
2162 ss.setAnnotationId(annotationId);
2163 ss.setViewerState(jarEntryName);
2164 ss.setGapped(model.gapped);
2165 ss.setTitle(model.title);
2166 // rna.addSecondaryStructure(ss);
2167 rna.getSecondaryStructure().add(ss);
2176 * Copy the contents of a file to a new entry added to the output jar
2180 * @param jarEntryName
2182 * additional identifying info to log to the console
2184 protected void copyFileToJar(JarOutputStream jout, String infilePath,
2185 String jarEntryName, String msg)
2187 try (InputStream is = new FileInputStream(infilePath))
2189 File file = new File(infilePath);
2190 if (file.exists() && jout != null)
2192 jalview.bin.Console.outPrintln(
2193 "Writing jar entry " + jarEntryName + " (" + msg + ")");
2194 jout.putNextEntry(new JarEntry(jarEntryName));
2197 // dis = new DataInputStream(new FileInputStream(file));
2198 // byte[] data = new byte[(int) file.length()];
2199 // dis.readFully(data);
2200 // writeJarEntry(jout, jarEntryName, data);
2202 } catch (Exception ex)
2204 ex.printStackTrace();
2209 * Copies input to output, in 4K buffers; handles any data (text or binary)
2213 * @throws IOException
2215 protected void copyAll(InputStream in, OutputStream out)
2218 byte[] buffer = new byte[4096];
2220 while ((bytesRead = in.read(buffer)) != -1)
2222 out.write(buffer, 0, bytesRead);
2227 * Save the state of a structure viewer
2232 * the archive XML element under which to save the state
2235 * @param matchedFile
2239 protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
2240 Pdbids pdb, PDBEntry entry, List<String> viewIds,
2241 String matchedFile, JalviewStructureDisplayI viewFrame)
2243 final AAStructureBindingModel bindingModel = viewFrame.getBinding();
2246 * Look for any bindings for this viewer to the PDB file of interest
2247 * (including part matches excluding chain id)
2249 for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
2251 final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
2252 final String pdbId = pdbentry.getId();
2253 if (!pdbId.equals(entry.getId()) && !(entry.getId().length() > 4
2254 && entry.getId().toLowerCase(Locale.ROOT)
2255 .startsWith(pdbId.toLowerCase(Locale.ROOT))))
2258 * not interested in a binding to a different PDB entry here
2262 if (matchedFile == null)
2264 matchedFile = pdbentry.getFile();
2266 else if (!matchedFile.equals(pdbentry.getFile()))
2269 "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
2270 + pdbentry.getFile());
2274 // can get at it if the ID
2275 // match is ambiguous (e.g.
2278 for (int smap = 0; smap < viewFrame.getBinding()
2279 .getSequence()[peid].length; smap++)
2281 // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
2282 if (jds == viewFrame.getBinding().getSequence()[peid][smap])
2284 StructureState state = new StructureState();
2285 state.setVisible(true);
2286 state.setXpos(viewFrame.getY());
2287 state.setYpos(viewFrame.getY());
2288 state.setWidth(viewFrame.getWidth());
2289 state.setHeight(viewFrame.getHeight());
2290 final String viewId = viewFrame.getViewId();
2291 state.setViewId(viewId);
2292 state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
2293 state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap));
2294 state.setColourByJmol(viewFrame.isColouredByViewer());
2295 state.setType(viewFrame.getViewerType().toString());
2296 // pdb.addStructureState(state);
2297 pdb.getStructureState().add(state);
2305 * Populates the AnnotationColourScheme xml for save. This captures the
2306 * settings of the options in the 'Colour by Annotation' dialog.
2309 * @param userColours
2313 private AnnotationColourScheme constructAnnotationColours(
2314 AnnotationColourGradient acg, List<UserColourScheme> userColours,
2317 AnnotationColourScheme ac = new AnnotationColourScheme();
2318 ac.setAboveThreshold(acg.getAboveThreshold());
2319 ac.setThreshold(acg.getAnnotationThreshold());
2320 // 2.10.2 save annotationId (unique) not annotation label
2321 ac.setAnnotation(acg.getAnnotation().annotationId);
2322 if (acg.getBaseColour() instanceof UserColourScheme)
2325 setUserColourScheme(acg.getBaseColour(), userColours, jm));
2330 ColourSchemeProperty.getColourName(acg.getBaseColour()));
2333 ac.setMaxColour(acg.getMaxColour().getRGB());
2334 ac.setMinColour(acg.getMinColour().getRGB());
2335 ac.setPerSequence(acg.isSeqAssociated());
2336 ac.setPredefinedColours(acg.isPredefinedColours());
2340 private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
2341 IdentityHashMap<SequenceGroup, String> groupRefs,
2342 AlignmentViewport av, Set<String> calcIdSet, boolean storeDS,
2343 SequenceSet vamsasSet)
2346 for (int i = 0; i < aa.length; i++)
2348 Annotation an = new Annotation();
2350 AlignmentAnnotation annotation = aa[i];
2351 if (annotation.annotationId != null)
2353 annotationIds.put(annotation.annotationId, annotation);
2356 an.setId(annotation.annotationId);
2358 an.setVisible(annotation.visible);
2360 an.setDescription(annotation.description);
2362 if (annotation.sequenceRef != null)
2364 // 2.9 JAL-1781 xref on sequence id rather than name
2365 an.setSequenceRef(seqsToIds.get(annotation.sequenceRef));
2367 if (annotation.groupRef != null)
2369 String groupIdr = groupRefs.get(annotation.groupRef);
2370 if (groupIdr == null)
2372 // make a locally unique String
2373 groupRefs.put(annotation.groupRef,
2374 groupIdr = ("" + System.currentTimeMillis()
2375 + annotation.groupRef.getName()
2376 + groupRefs.size()));
2378 an.setGroupRef(groupIdr.toString());
2381 // store all visualization attributes for annotation
2382 an.setGraphHeight(annotation.graphHeight);
2383 an.setCentreColLabels(annotation.centreColLabels);
2384 an.setScaleColLabels(annotation.scaleColLabel);
2385 an.setShowAllColLabels(annotation.showAllColLabels);
2386 an.setBelowAlignment(annotation.belowAlignment);
2388 if (annotation.graph > 0)
2391 an.setGraphType(annotation.graph);
2392 an.setGraphGroup(annotation.graphGroup);
2393 if (annotation.getThreshold() != null)
2395 ThresholdLine line = new ThresholdLine();
2396 line.setLabel(annotation.getThreshold().label);
2397 line.setValue(annotation.getThreshold().value);
2398 line.setColour(annotation.getThreshold().colour.getRGB());
2399 an.setThresholdLine(line);
2401 if (annotation.graph == AlignmentAnnotation.CONTACT_MAP)
2403 if (annotation.sequenceRef.getContactMaps() != null)
2405 ContactMatrixI cm = annotation.sequenceRef
2406 .getContactMatrixFor(annotation);
2409 storeMatrixFor(vamsasSet, an, annotation, cm);
2419 an.setLabel(annotation.label);
2421 if (annotation == av.getAlignmentQualityAnnot()
2422 || annotation == av.getAlignmentConservationAnnotation()
2423 || annotation == av.getAlignmentConsensusAnnotation()
2424 || annotation.autoCalculated)
2426 // new way of indicating autocalculated annotation -
2427 an.setAutoCalculated(annotation.autoCalculated);
2429 if (annotation.hasScore())
2431 an.setScore(annotation.getScore());
2434 if (annotation.getCalcId() != null)
2436 calcIdSet.add(annotation.getCalcId());
2437 an.setCalcId(annotation.getCalcId());
2439 if (annotation.hasProperties())
2441 for (String pr : annotation.getProperties())
2443 jalview.xml.binding.jalview.Property prop = new jalview.xml.binding.jalview.Property();
2445 prop.setValue(annotation.getProperty(pr));
2446 an.getProperty().add(prop);
2450 AnnotationElement ae;
2451 if (annotation.annotations != null)
2453 an.setScoreOnly(false);
2454 for (int a = 0; a < annotation.annotations.length; a++)
2456 if ((annotation == null) || (annotation.annotations[a] == null))
2461 ae = new AnnotationElement();
2462 if (annotation.annotations[a].description != null)
2464 ae.setDescription(annotation.annotations[a].description);
2466 if (annotation.annotations[a].displayCharacter != null)
2468 ae.setDisplayCharacter(
2469 annotation.annotations[a].displayCharacter);
2472 if (!Float.isNaN(annotation.annotations[a].value))
2474 ae.setValue(annotation.annotations[a].value);
2478 if (annotation.annotations[a].secondaryStructure > ' ')
2480 ae.setSecondaryStructure(
2481 annotation.annotations[a].secondaryStructure + "");
2484 if (annotation.annotations[a].colour != null
2485 && annotation.annotations[a].colour != java.awt.Color.black)
2487 ae.setColour(annotation.annotations[a].colour.getRGB());
2490 // an.addAnnotationElement(ae);
2491 an.getAnnotationElement().add(ae);
2492 if (annotation.autoCalculated)
2494 // only write one non-null entry into the annotation row -
2495 // sufficient to get the visualization attributes necessary to
2503 an.setScoreOnly(true);
2505 if (!storeDS || (storeDS && !annotation.autoCalculated))
2507 // skip autocalculated annotation - these are only provided for
2509 // vamsasSet.addAnnotation(an);
2510 vamsasSet.getAnnotation().add(an);
2516 private void storeMatrixFor(SequenceSet root, Annotation an,
2517 AlignmentAnnotation annotation, ContactMatrixI cm)
2519 String cmId = contactMatrices.get(cm);
2520 MatrixType xmlmat = null;
2522 // first create an xml ref for the matrix data, if none exist
2525 xmlmat = new MatrixType();
2526 xmlmat.setType(cm.getType());
2527 xmlmat.setRows(BigInteger.valueOf(cm.getWidth()));
2528 xmlmat.setCols(BigInteger.valueOf(cm.getHeight()));
2529 // consider using an opaque to/from -> allow instance to control
2530 // its representation ?
2531 xmlmat.setElements(ContactMatrix.contactToFloatString(cm));
2534 for (BitSet gp : cm.getGroups())
2536 xmlmat.getGroups().add(stringifyBitset(gp));
2541 // provenance object for tree ?
2542 xmlmat.getNewick().add(cm.getNewick());
2543 xmlmat.setTreeMethod(cm.getTreeMethod());
2545 if (cm.hasCutHeight())
2547 xmlmat.setCutHeight(cm.getCutHeight());
2549 xmlmat.setId(cmId = "m" + contactMatrices.size()
2550 + System.currentTimeMillis());
2551 Console.trace("Matrix data stored :" + cmId);
2552 contactMatrices.put(cm, cmId);
2553 contactMatrixRefs.put(cmId, cm);
2554 xmlMatrices.add(xmlmat);
2558 Console.trace("Existing Matrix stored :" + cmId);
2561 // now store mapping
2563 MapOnAMatrixType xmlmatmapping = new MapOnAMatrixType();
2564 xmlmatmapping.setMatrix(cmId);
2566 // Pretty much all matrices currently managed in this way are
2567 // mappableContactMatrixI implementations - but check anyway
2568 if (cm instanceof MappableContactMatrixI)
2570 jalview.util.MapList mlst = ((MappableContactMatrixI) cm)
2571 .getMapFor(annotation.sequenceRef);
2574 MapListType mp = new MapListType();
2575 List<int[]> r = mlst.getFromRanges();
2576 for (int[] range : r)
2578 MapListFrom mfrom = new MapListFrom();
2579 mfrom.setStart(range[0]);
2580 mfrom.setEnd(range[1]);
2581 // mp.addMapListFrom(mfrom);
2582 mp.getMapListFrom().add(mfrom);
2584 r = mlst.getToRanges();
2585 for (int[] range : r)
2587 MapListTo mto = new MapListTo();
2588 mto.setStart(range[0]);
2589 mto.setEnd(range[1]);
2590 // mp.addMapListTo(mto);
2591 mp.getMapListTo().add(mto);
2593 mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
2594 mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
2595 xmlmatmapping.setMapping(mp);
2599 an.getContactmatrix().add(xmlmatmapping);
2602 private String stringifyBitset(BitSet gp)
2604 StringBuilder sb = new StringBuilder();
2605 for (long val : gp.toLongArray())
2607 if (sb.length() > 0)
2613 return sb.toString();
2616 private BitSet deStringifyBitset(String stringified)
2618 if ("".equals(stringified) || stringified == null)
2620 return new BitSet();
2622 String[] longvals = stringified.split(",");
2623 long[] newlongvals = new long[longvals.length];
2624 for (int lv = 0; lv < longvals.length; lv++)
2628 newlongvals[lv] = Long.valueOf(longvals[lv]);
2629 } catch (Exception x)
2631 errorMessage += "Couldn't destringify bitset from: '" + stringified
2633 newlongvals[lv] = 0;
2636 return BitSet.valueOf(newlongvals);
2640 private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
2642 AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
2643 if (settings != null)
2645 CalcIdParam vCalcIdParam = new CalcIdParam();
2646 vCalcIdParam.setCalcId(calcId);
2647 // vCalcIdParam.addServiceURL(settings.getServiceURI());
2648 vCalcIdParam.getServiceURL().add(settings.getServiceURI());
2649 // generic URI allowing a third party to resolve another instance of the
2650 // service used for this calculation
2651 for (String url : settings.getServiceURLs())
2653 // vCalcIdParam.addServiceURL(urls);
2654 vCalcIdParam.getServiceURL().add(url);
2656 vCalcIdParam.setVersion("1.0");
2657 if (settings.getPreset() != null)
2659 WsParamSetI setting = settings.getPreset();
2660 vCalcIdParam.setName(setting.getName());
2661 vCalcIdParam.setDescription(setting.getDescription());
2665 vCalcIdParam.setName("");
2666 vCalcIdParam.setDescription("Last used parameters");
2668 // need to be able to recover 1) settings 2) user-defined presets or
2669 // recreate settings from preset 3) predefined settings provided by
2670 // service - or settings that can be transferred (or discarded)
2671 vCalcIdParam.setParameters(
2672 settings.getWsParamFile().replace("\n", "|\\n|"));
2673 vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
2674 // todo - decide if updateImmediately is needed for any projects.
2676 return vCalcIdParam;
2681 private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
2684 if (calcIdParam.getVersion().equals("1.0"))
2686 final String[] calcIds = calcIdParam.getServiceURL()
2687 .toArray(new String[0]);
2688 Jws2Instance service = Jws2Discoverer.getDiscoverer()
2689 .getPreferredServiceFor(calcIds);
2690 if (service != null)
2692 WsParamSetI parmSet = null;
2695 parmSet = service.getParamStore().parseServiceParameterFile(
2696 calcIdParam.getName(), calcIdParam.getDescription(),
2698 calcIdParam.getParameters().replace("|\\n|", "\n"));
2699 } catch (IOException x)
2701 Console.warn("Couldn't parse parameter data for "
2702 + calcIdParam.getCalcId(), x);
2705 List<ArgumentI> argList = null;
2706 if (calcIdParam.getName().length() > 0)
2708 parmSet = service.getParamStore()
2709 .getPreset(calcIdParam.getName());
2710 if (parmSet != null)
2712 // TODO : check we have a good match with settings in AACon -
2713 // otherwise we'll need to create a new preset
2718 argList = parmSet.getArguments();
2721 AAConSettings settings = new AAConSettings(
2722 calcIdParam.isAutoUpdate(), service, parmSet, argList);
2723 av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
2724 calcIdParam.isNeedsUpdate());
2730 "Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
2734 throw new Error(MessageManager.formatMessage(
2735 "error.unsupported_version_calcIdparam", new Object[]
2736 { calcIdParam.toString() }));
2740 * External mapping between jalview objects and objects yielding a valid and
2741 * unique object ID string. This is null for normal Jalview project IO, but
2742 * non-null when a jalview project is being read or written as part of a
2745 IdentityHashMap jv2vobj = null;
2748 * Construct a unique ID for jvobj using either existing bindings or if none
2749 * exist, the result of the hashcode call for the object.
2752 * jalview data object
2753 * @return unique ID for referring to jvobj
2755 private String makeHashCode(Object jvobj, String altCode)
2757 if (jv2vobj != null)
2759 Object id = jv2vobj.get(jvobj);
2762 return id.toString();
2764 // check string ID mappings
2765 if (jvids2vobj != null && jvobj instanceof String)
2767 id = jvids2vobj.get(jvobj);
2771 return id.toString();
2773 // give up and warn that something has gone wrong
2775 "Cannot find ID for object in external mapping : " + jvobj);
2781 * return local jalview object mapped to ID, if it exists
2785 * @return null or object bound to idcode
2787 private Object retrieveExistingObj(String idcode)
2789 if (idcode != null && vobj2jv != null)
2791 return vobj2jv.get(idcode);
2797 * binding from ID strings from external mapping table to jalview data model
2800 private Hashtable vobj2jv;
2802 private Sequence createVamsasSequence(String id, SequenceI jds)
2804 return createVamsasSequence(true, id, jds, null);
2807 private Sequence createVamsasSequence(boolean recurse, String id,
2808 SequenceI jds, SequenceI parentseq)
2810 Sequence vamsasSeq = new Sequence();
2811 vamsasSeq.setId(id);
2812 vamsasSeq.setName(jds.getName());
2813 vamsasSeq.setSequence(jds.getSequenceAsString());
2814 vamsasSeq.setDescription(jds.getDescription());
2815 List<DBRefEntry> dbrefs = null;
2816 if (jds.getDatasetSequence() != null)
2818 vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
2822 // seqId==dsseqid so we can tell which sequences really are
2823 // dataset sequences only
2824 vamsasSeq.setDsseqid(id);
2825 dbrefs = jds.getDBRefs();
2826 if (parentseq == null)
2833 * save any dbrefs; special subclass GeneLocus is flagged as 'locus'
2837 for (int d = 0, nd = dbrefs.size(); d < nd; d++)
2839 DBRef dbref = new DBRef();
2840 DBRefEntry ref = dbrefs.get(d);
2841 dbref.setSource(ref.getSource());
2842 dbref.setVersion(ref.getVersion());
2843 dbref.setAccessionId(ref.getAccessionId());
2844 dbref.setCanonical(ref.isCanonical());
2845 if (ref instanceof GeneLocus)
2847 dbref.setLocus(true);
2851 Mapping mp = createVamsasMapping(ref.getMap(), parentseq, jds,
2853 dbref.setMapping(mp);
2855 vamsasSeq.getDBRef().add(dbref);
2861 private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
2862 SequenceI parentseq, SequenceI jds, boolean recurse)
2865 if (jmp.getMap() != null)
2869 jalview.util.MapList mlst = jmp.getMap();
2870 List<int[]> r = mlst.getFromRanges();
2871 for (int[] range : r)
2873 MapListFrom mfrom = new MapListFrom();
2874 mfrom.setStart(range[0]);
2875 mfrom.setEnd(range[1]);
2876 // mp.addMapListFrom(mfrom);
2877 mp.getMapListFrom().add(mfrom);
2879 r = mlst.getToRanges();
2880 for (int[] range : r)
2882 MapListTo mto = new MapListTo();
2883 mto.setStart(range[0]);
2884 mto.setEnd(range[1]);
2885 // mp.addMapListTo(mto);
2886 mp.getMapListTo().add(mto);
2888 mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
2889 mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
2890 if (jmp.getTo() != null)
2892 // MappingChoice mpc = new MappingChoice();
2894 // check/create ID for the sequence referenced by getTo()
2897 SequenceI ps = null;
2898 if (parentseq != jmp.getTo()
2899 && parentseq.getDatasetSequence() != jmp.getTo())
2901 // chaining dbref rather than a handshaking one
2902 jmpid = seqHash(ps = jmp.getTo());
2906 jmpid = seqHash(ps = parentseq);
2908 // mpc.setDseqFor(jmpid);
2909 mp.setDseqFor(jmpid);
2910 if (!seqRefIds.containsKey(jmpid))
2912 Console.debug("creatign new DseqFor ID");
2913 seqRefIds.put(jmpid, ps);
2917 Console.debug("reusing DseqFor ID");
2920 // mp.setMappingChoice(mpc);
2926 String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
2927 List<UserColourScheme> userColours, JalviewModel jm)
2930 jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
2931 boolean newucs = false;
2932 if (!userColours.contains(ucs))
2934 userColours.add(ucs);
2937 id = "ucs" + userColours.indexOf(ucs);
2940 // actually create the scheme's entry in the XML model
2941 java.awt.Color[] colours = ucs.getColours();
2942 UserColours uc = new UserColours();
2943 // UserColourScheme jbucs = new UserColourScheme();
2944 JalviewUserColours jbucs = new JalviewUserColours();
2946 for (int i = 0; i < colours.length; i++)
2948 Colour col = new Colour();
2949 col.setName(ResidueProperties.aa[i]);
2950 col.setRGB(jalview.util.Format.getHexString(colours[i]));
2951 // jbucs.addColour(col);
2952 jbucs.getColour().add(col);
2954 if (ucs.getLowerCaseColours() != null)
2956 colours = ucs.getLowerCaseColours();
2957 for (int i = 0; i < colours.length; i++)
2959 Colour col = new Colour();
2960 col.setName(ResidueProperties.aa[i].toLowerCase(Locale.ROOT));
2961 col.setRGB(jalview.util.Format.getHexString(colours[i]));
2962 // jbucs.addColour(col);
2963 jbucs.getColour().add(col);
2968 uc.setUserColourScheme(jbucs);
2969 // jm.addUserColours(uc);
2970 jm.getUserColours().add(uc);
2976 jalview.schemes.UserColourScheme getUserColourScheme(JalviewModel jm,
2979 List<UserColours> uc = jm.getUserColours();
2980 UserColours colours = null;
2982 for (int i = 0; i < uc.length; i++)
2984 if (uc[i].getId().equals(id))
2991 for (UserColours c : uc)
2993 if (c.getId().equals(id))
3000 java.awt.Color[] newColours = new java.awt.Color[24];
3002 for (int i = 0; i < 24; i++)
3004 newColours[i] = new java.awt.Color(Integer.parseInt(
3005 // colours.getUserColourScheme().getColour(i).getRGB(), 16));
3006 colours.getUserColourScheme().getColour().get(i).getRGB(),
3010 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
3013 if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24)
3015 newColours = new java.awt.Color[23];
3016 for (int i = 0; i < 23; i++)
3018 newColours[i] = new java.awt.Color(
3019 Integer.parseInt(colours.getUserColourScheme().getColour()
3020 .get(i + 24).getRGB(), 16));
3022 ucs.setLowerCaseColours(newColours);
3029 * contains last error message (if any) encountered by XML loader.
3031 String errorMessage = null;
3034 * flag to control whether the Jalview2XML_V1 parser should be deferred to if
3035 * exceptions are raised during project XML parsing
3037 public boolean attemptversion1parse = false;
3040 * Load a jalview project archive from a jar file
3043 * - HTTP URL or filename
3045 public AlignFrame loadJalviewAlign(final Object file)
3048 jalview.gui.AlignFrame af = null;
3052 // create list to store references for any new Jmol viewers created
3053 newStructureViewers = new Vector<>();
3054 // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
3055 // Workaround is to make sure caller implements the JarInputStreamProvider
3057 // so we can re-open the jar input stream for each entry.
3059 jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
3060 af = loadJalviewAlign(jprovider);
3063 af.setMenusForViewport();
3065 } catch (MalformedURLException e)
3067 errorMessage = "Invalid URL format for '" + file + "'";
3073 SwingUtilities.invokeAndWait(new Runnable()
3078 setLoadingFinishedForNewStructureViewers();
3081 } catch (Exception x)
3084 .errPrintln("Error loading alignment: " + x.getMessage());
3090 @SuppressWarnings("unused")
3091 private jarInputStreamProvider createjarInputStreamProvider(
3092 final Object ofile) throws MalformedURLException
3095 // BH 2018 allow for bytes already attached to File object
3098 String file = (ofile instanceof File
3099 ? ((File) ofile).getCanonicalPath()
3100 : ofile.toString());
3101 byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
3104 errorMessage = null;
3105 uniqueSetSuffix = null;
3107 viewportsAdded.clear();
3108 frefedSequence = null;
3110 if (HttpUtils.startsWithHttpOrHttps(file))
3112 url = new URL(file);
3114 final URL _url = url;
3115 return new jarInputStreamProvider()
3119 public JarInputStream getJarInputStream() throws IOException
3123 // jalview.bin.Console.outPrintln("Jalview2XML: opening byte
3124 // jarInputStream for
3125 // bytes.length=" + bytes.length);
3126 return new JarInputStream(new ByteArrayInputStream(bytes));
3130 // jalview.bin.Console.outPrintln("Jalview2XML: opening url
3131 // jarInputStream for "
3133 return new JarInputStream(_url.openStream());
3137 // jalview.bin.Console.outPrintln("Jalview2XML: opening file
3138 // jarInputStream for
3140 return new JarInputStream(new FileInputStream(file));
3145 public String getFilename()
3150 } catch (IOException e)
3152 e.printStackTrace();
3158 * Recover jalview session from a jalview project archive. Caller may
3159 * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
3160 * themselves. Any null fields will be initialised with default values,
3161 * non-null fields are left alone.
3166 public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
3168 errorMessage = null;
3169 if (uniqueSetSuffix == null)
3171 uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
3173 if (seqRefIds == null)
3177 AlignFrame af = null, _af = null;
3178 List<AlignFrame> toRepaint = new ArrayList<AlignFrame>();
3179 IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
3180 Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
3181 final String file = jprovider.getFilename();
3184 JarInputStream jin = null;
3185 JarEntry jarentry = null;
3190 jin = jprovider.getJarInputStream();
3191 for (int i = 0; i < entryCount; i++)
3193 jarentry = jin.getNextJarEntry();
3196 if (jarentry != null && jarentry.getName().endsWith(".xml"))
3198 JAXBContext jc = JAXBContext
3199 .newInstance("jalview.xml.binding.jalview");
3200 XMLStreamReader streamReader = XMLInputFactory.newInstance()
3201 .createXMLStreamReader(jin);
3202 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
3203 JAXBElement<JalviewModel> jbe = um.unmarshal(streamReader,
3204 JalviewModel.class);
3205 JalviewModel object = jbe.getValue();
3207 if (true) // !skipViewport(object))
3209 _af = loadFromObject(object, file, true, jprovider);
3210 if (_af != null && object.getViewport().size() > 0)
3211 // getJalviewModelSequence().getViewportCount() > 0)
3216 // store a reference to the first view
3219 if (_af.getViewport().isGatherViewsHere())
3221 // if this is a gathered view, keep its reference since
3222 // after gathering views, only this frame will remain
3224 gatherToThisFrame.put(_af.getViewport().getSequenceSetId(),
3227 // Save dataset to register mappings once all resolved
3228 importedDatasets.put(
3229 af.getViewport().getAlignment().getDataset(),
3230 af.getViewport().getAlignment().getDataset());
3235 else if (jarentry != null)
3237 // Some other file here.
3240 } while (jarentry != null);
3242 resolveFrefedSequences();
3243 for (AlignFrame alignFrame : toRepaint)
3245 alignFrame.repaint();
3247 } catch (IOException ex)
3249 ex.printStackTrace();
3250 errorMessage = "Couldn't locate Jalview XML file : " + file;
3251 jalview.bin.Console.errPrintln(
3252 "Exception whilst loading jalview XML file : " + ex + "\n");
3253 } catch (Exception ex)
3256 .errPrintln("Parsing as Jalview Version 2 file failed.");
3257 ex.printStackTrace(System.err);
3258 if (attemptversion1parse)
3260 // used to attempt to parse as V1 castor-generated xml
3262 if (Desktop.instance != null)
3264 Desktop.instance.stopLoading();
3268 jalview.bin.Console.outPrintln("Successfully loaded archive file");
3271 ex.printStackTrace();
3273 jalview.bin.Console.errPrintln(
3274 "Exception whilst loading jalview XML file : " + ex + "\n");
3275 } catch (OutOfMemoryError e)
3277 // Don't use the OOM Window here
3278 errorMessage = "Out of memory loading jalview XML file";
3280 .errPrintln("Out of memory whilst loading jalview XML file");
3281 e.printStackTrace();
3285 * Regather multiple views (with the same sequence set id) to the frame (if
3286 * any) that is flagged as the one to gather to, i.e. convert them to tabbed
3287 * views instead of separate frames. Note this doesn't restore a state where
3288 * some expanded views in turn have tabbed views - the last "first tab" read
3289 * in will play the role of gatherer for all.
3291 for (AlignFrame fr : gatherToThisFrame.values())
3293 Desktop.instance.gatherViews(fr);
3296 restoreSplitFrames();
3297 for (AlignmentI ds : importedDatasets.keySet())
3299 if (ds.getCodonFrames() != null)
3301 StructureSelectionManager
3302 .getStructureSelectionManager(Desktop.instance)
3303 .registerMappings(ds.getCodonFrames());
3306 if (errorMessage != null)
3311 if (Desktop.instance != null)
3313 Desktop.instance.stopLoading();
3320 * Try to reconstruct and display SplitFrame windows, where each contains
3321 * complementary dna and protein alignments. Done by pairing up AlignFrame
3322 * objects (created earlier) which have complementary viewport ids associated.
3324 protected void restoreSplitFrames()
3326 List<SplitFrame> gatherTo = new ArrayList<>();
3327 List<AlignFrame> addedToSplitFrames = new ArrayList<>();
3328 Map<String, AlignFrame> dna = new HashMap<>();
3331 * Identify the DNA alignments
3333 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
3336 AlignFrame af = candidate.getValue();
3337 if (af.getViewport().getAlignment().isNucleotide())
3339 dna.put(candidate.getKey().getId(), af);
3344 * Try to match up the protein complements
3346 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
3349 AlignFrame af = candidate.getValue();
3350 if (!af.getViewport().getAlignment().isNucleotide())
3352 String complementId = candidate.getKey().getComplementId();
3353 // only non-null complements should be in the Map
3354 if (complementId != null && dna.containsKey(complementId))
3356 final AlignFrame dnaFrame = dna.get(complementId);
3357 SplitFrame sf = createSplitFrame(dnaFrame, af);
3358 addedToSplitFrames.add(dnaFrame);
3359 addedToSplitFrames.add(af);
3360 dnaFrame.setMenusForViewport();
3361 af.setMenusForViewport();
3362 if (af.getViewport().isGatherViewsHere())
3371 * Open any that we failed to pair up (which shouldn't happen!) as
3372 * standalone AlignFrame's.
3374 for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
3377 AlignFrame af = candidate.getValue();
3378 if (!addedToSplitFrames.contains(af))
3380 Viewport view = candidate.getKey();
3381 Desktop.addInternalFrame(af, view.getTitle(),
3382 safeInt(view.getWidth()), safeInt(view.getHeight()));
3383 af.setMenusForViewport();
3384 jalview.bin.Console.errPrintln("Failed to restore view "
3385 + view.getTitle() + " to split frame");
3390 * Gather back into tabbed views as flagged.
3392 for (SplitFrame sf : gatherTo)
3394 Desktop.instance.gatherViews(sf);
3397 splitFrameCandidates.clear();
3401 * Construct and display one SplitFrame holding DNA and protein alignments.
3404 * @param proteinFrame
3407 protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
3408 AlignFrame proteinFrame)
3410 SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
3411 String title = MessageManager.getString("label.linked_view_title");
3412 int width = (int) dnaFrame.getBounds().getWidth();
3413 int height = (int) (dnaFrame.getBounds().getHeight()
3414 + proteinFrame.getBounds().getHeight() + 50);
3417 * SplitFrame location is saved to both enclosed frames
3419 splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
3420 Desktop.addInternalFrame(splitFrame, title, width, height);
3423 * And compute cDNA consensus (couldn't do earlier with consensus as
3424 * mappings were not yet present)
3426 proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel);
3432 * check errorMessage for a valid error message and raise an error box in the
3433 * GUI or write the current errorMessage to stderr and then clear the error
3436 protected void reportErrors()
3438 reportErrors(false);
3441 protected void reportErrors(final boolean saving)
3443 if (errorMessage != null)
3445 final String finalErrorMessage = errorMessage;
3448 javax.swing.SwingUtilities.invokeLater(new Runnable()
3453 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3455 "Error " + (saving ? "saving" : "loading")
3457 JvOptionPane.WARNING_MESSAGE);
3463 jalview.bin.Console.errPrintln(
3464 "Problem loading Jalview file: " + errorMessage);
3467 errorMessage = null;
3470 Map<String, String> alreadyLoadedPDB = new HashMap<>();
3473 * when set, local views will be updated from view stored in JalviewXML
3474 * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
3475 * sync if this is set to true.
3477 private final boolean updateLocalViews = false;
3480 * Returns the path to a temporary file holding the PDB file for the given PDB
3481 * id. The first time of asking, searches for a file of that name in the
3482 * Jalview project jar, and copies it to a new temporary file. Any repeat
3483 * requests just return the path to the file previously created.
3489 String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
3492 if (alreadyLoadedPDB.containsKey(pdbId))
3494 return alreadyLoadedPDB.get(pdbId).toString();
3497 String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
3499 if (tempFile != null)
3501 alreadyLoadedPDB.put(pdbId, tempFile);
3507 * Copies the jar entry of given name to a new temporary file and returns the
3508 * path to the file, or null if the entry is not found.
3511 * @param jarEntryName
3513 * a prefix for the temporary file name, must be at least three
3515 * @param suffixModel
3516 * null or original file - so new file can be given the same suffix
3520 protected String copyJarEntry(jarInputStreamProvider jprovider,
3521 String jarEntryName, String prefix, String suffixModel)
3523 String suffix = ".tmp";
3524 if (suffixModel == null)
3526 suffixModel = jarEntryName;
3528 int sfpos = suffixModel.lastIndexOf(".");
3529 if (sfpos > -1 && sfpos < (suffixModel.length() - 1))
3531 suffix = "." + suffixModel.substring(sfpos + 1);
3534 try (JarInputStream jin = jprovider.getJarInputStream())
3536 JarEntry entry = null;
3539 entry = jin.getNextJarEntry();
3540 } while (entry != null && !entry.getName().equals(jarEntryName));
3544 // in = new BufferedReader(new InputStreamReader(jin, UTF_8));
3545 File outFile = File.createTempFile(prefix, suffix);
3546 outFile.deleteOnExit();
3547 try (OutputStream os = new FileOutputStream(outFile))
3551 String t = outFile.getAbsolutePath();
3557 "Couldn't find entry in Jalview Jar for " + jarEntryName);
3559 } catch (Exception ex)
3561 ex.printStackTrace();
3567 private class JvAnnotRow
3569 public JvAnnotRow(int i, AlignmentAnnotation jaa)
3576 * persisted version of annotation row from which to take vis properties
3578 public jalview.datamodel.AlignmentAnnotation template;
3581 * original position of the annotation row in the alignment
3587 * Load alignment frame from jalview XML DOM object
3589 * @param jalviewModel
3592 * filename source string
3593 * @param loadTreesAndStructures
3594 * when false only create Viewport
3596 * data source provider
3597 * @return alignment frame created from view stored in DOM
3599 AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
3600 boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
3602 SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet()
3604 List<Sequence> vamsasSeqs = vamsasSet.getSequence();
3606 // JalviewModelSequence jms = object.getJalviewModelSequence();
3608 // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
3610 Viewport view = (jalviewModel.getViewport().size() > 0)
3611 ? jalviewModel.getViewport().get(0)
3614 // ////////////////////////////////
3615 // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
3618 // If we just load in the same jar file again, the sequenceSetId
3619 // will be the same, and we end up with multiple references
3620 // to the same sequenceSet. We must modify this id on load
3621 // so that each load of the file gives a unique id
3624 * used to resolve correct alignment dataset for alignments with multiple
3627 String uniqueSeqSetId = null;
3628 String viewId = null;
3631 uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
3632 viewId = (view.getId() == null ? null
3633 : view.getId() + uniqueSetSuffix);
3636 // ////////////////////////////////
3637 // LOAD MATRICES (IF ANY)
3639 if (vamsasSet.getMatrix() != null && vamsasSet.getMatrix().size() > 0)
3641 importMatrixData(vamsasSet.getMatrix());
3644 // ////////////////////////////////
3647 List<SequenceI> hiddenSeqs = null;
3649 List<SequenceI> tmpseqs = new ArrayList<>();
3651 boolean multipleView = false;
3652 SequenceI referenceseqForView = null;
3653 // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
3654 List<JSeq> jseqs = jalviewModel.getJSeq();
3655 int vi = 0; // counter in vamsasSeq array
3656 for (int i = 0; i < jseqs.size(); i++)
3658 JSeq jseq = jseqs.get(i);
3659 String seqId = jseq.getId();
3661 SequenceI tmpSeq = seqRefIds.get(seqId);
3664 if (!incompleteSeqs.containsKey(seqId))
3666 // may not need this check, but keep it for at least 2.9,1 release
3667 if (tmpSeq.getStart() != jseq.getStart()
3668 || tmpSeq.getEnd() != jseq.getEnd())
3670 jalview.bin.Console.errPrintln(String.format(
3671 "Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
3672 tmpSeq.getName(), tmpSeq.getStart(), tmpSeq.getEnd(),
3673 jseq.getStart(), jseq.getEnd()));
3678 incompleteSeqs.remove(seqId);
3680 if (vamsasSeqs.size() > vi
3681 && vamsasSeqs.get(vi).getId().equals(seqId))
3683 // most likely we are reading a dataset XML document so
3684 // update from vamsasSeq section of XML for this sequence
3685 tmpSeq.setName(vamsasSeqs.get(vi).getName());
3686 tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription());
3687 tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence());
3692 // reading multiple views, so vamsasSeq set is a subset of JSeq
3693 multipleView = true;
3695 tmpSeq.setStart(jseq.getStart());
3696 tmpSeq.setEnd(jseq.getEnd());
3697 tmpseqs.add(tmpSeq);
3701 Sequence vamsasSeq = vamsasSeqs.get(vi);
3702 tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(),
3703 vamsasSeq.getSequence());
3704 tmpSeq.setDescription(vamsasSeq.getDescription());
3705 tmpSeq.setStart(jseq.getStart());
3706 tmpSeq.setEnd(jseq.getEnd());
3707 tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
3708 seqRefIds.put(vamsasSeq.getId(), tmpSeq);
3709 tmpseqs.add(tmpSeq);
3713 if (safeBoolean(jseq.isViewreference()))
3715 referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
3718 if (jseq.isHidden() != null && jseq.isHidden().booleanValue())
3720 if (hiddenSeqs == null)
3722 hiddenSeqs = new ArrayList<>();
3725 hiddenSeqs.add(tmpSeq);
3730 // Create the alignment object from the sequence set
3731 // ///////////////////////////////
3732 SequenceI[] orderedSeqs = tmpseqs
3733 .toArray(new SequenceI[tmpseqs.size()]);
3735 AlignmentI al = null;
3736 // so we must create or recover the dataset alignment before going further
3737 // ///////////////////////////////
3738 if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
3740 // older jalview projects do not have a dataset - so creat alignment and
3742 al = new Alignment(orderedSeqs);
3743 al.setDataset(null);
3747 boolean isdsal = jalviewModel.getViewport().isEmpty();
3750 // we are importing a dataset record, so
3751 // recover reference to an alignment already materialsed as dataset
3752 al = getDatasetFor(vamsasSet.getDatasetId());
3756 // materialse the alignment
3757 al = new Alignment(orderedSeqs);
3761 addDatasetRef(vamsasSet.getDatasetId(), al);
3764 // finally, verify all data in vamsasSet is actually present in al
3765 // passing on flag indicating if it is actually a stored dataset
3766 recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
3769 if (referenceseqForView != null)
3771 al.setSeqrep(referenceseqForView);
3773 // / Add the alignment properties
3774 for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++)
3776 SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties()
3778 al.setProperty(ssp.getKey(), ssp.getValue());
3781 // ///////////////////////////////
3783 Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
3786 // load sequence features, database references and any associated PDB
3787 // structures for the alignment
3789 // prior to 2.10, this part would only be executed the first time a
3790 // sequence was encountered, but not afterwards.
3791 // now, for 2.10 projects, this is also done if the xml doc includes
3792 // dataset sequences not actually present in any particular view.
3794 for (int i = 0; i < vamsasSeqs.size(); i++)
3796 JSeq jseq = jseqs.get(i);
3797 if (jseq.getFeatures().size() > 0)
3799 List<Feature> features = jseq.getFeatures();
3800 for (int f = 0; f < features.size(); f++)
3802 Feature feat = features.get(f);
3803 SequenceFeature sf = new SequenceFeature(feat.getType(),
3804 feat.getDescription(), feat.getBegin(), feat.getEnd(),
3805 safeFloat(feat.getScore()), feat.getFeatureGroup());
3806 sf.setStatus(feat.getStatus());
3809 * load any feature attributes - include map-valued attributes
3811 Map<String, Map<String, String>> mapAttributes = new HashMap<>();
3812 for (int od = 0; od < feat.getOtherData().size(); od++)
3814 OtherData keyValue = feat.getOtherData().get(od);
3815 String attributeName = keyValue.getKey();
3816 String attributeValue = keyValue.getValue();
3817 if (attributeName.startsWith("LINK"))
3819 sf.addLink(attributeValue);
3823 String subAttribute = keyValue.getKey2();
3824 if (subAttribute == null)
3826 // simple string-valued attribute
3827 sf.setValue(attributeName, attributeValue);
3831 // attribute 'key' has sub-attribute 'key2'
3832 if (!mapAttributes.containsKey(attributeName))
3834 mapAttributes.put(attributeName, new HashMap<>());
3836 mapAttributes.get(attributeName).put(subAttribute,
3841 for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
3844 sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
3847 // adds feature to datasequence's feature set (since Jalview 2.10)
3848 al.getSequenceAt(i).addSequenceFeature(sf);
3851 if (vamsasSeqs.get(i).getDBRef().size() > 0)
3853 // adds dbrefs to datasequence's set (since Jalview 2.10)
3855 al.getSequenceAt(i).getDatasetSequence() == null
3856 ? al.getSequenceAt(i)
3857 : al.getSequenceAt(i).getDatasetSequence(),
3860 if (jseq.getPdbids().size() > 0)
3862 List<Pdbids> ids = jseq.getPdbids();
3863 for (int p = 0; p < ids.size(); p++)
3865 Pdbids pdbid = ids.get(p);
3866 jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
3867 entry.setId(pdbid.getId());
3868 if (pdbid.getType() != null)
3870 if (PDBEntry.Type.getType(pdbid.getType()) != null)
3872 entry.setType(PDBEntry.Type.getType(pdbid.getType()));
3876 entry.setType(PDBEntry.Type.FILE);
3879 // jprovider is null when executing 'New View'
3880 if (pdbid.getFile() != null && jprovider != null)
3882 if (!pdbloaded.containsKey(pdbid.getFile()))
3884 entry.setFile(loadPDBFile(jprovider, pdbid.getId(),
3889 entry.setFile(pdbloaded.get(pdbid.getId()).toString());
3893 if (pdbid.getPdbentryItem() != null)
3895 for (PdbentryItem item : pdbid.getPdbentryItem())
3897 for (Property pr : item.getProperty())
3899 entry.setProperty(pr.getName(), pr.getValue());
3904 for (Property prop : pdbid.getProperty())
3906 entry.setProperty(prop.getName(), prop.getValue());
3908 StructureSelectionManager
3909 .getStructureSelectionManager(Desktop.instance)
3910 .registerPDBEntry(entry);
3911 // adds PDBEntry to datasequence's set (since Jalview 2.10)
3912 if (al.getSequenceAt(i).getDatasetSequence() != null)
3914 al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
3918 al.getSequenceAt(i).addPDBId(entry);
3923 } // end !multipleview
3925 // ///////////////////////////////
3926 // LOAD SEQUENCE MAPPINGS
3928 if (vamsasSet.getAlcodonFrame().size() > 0)
3930 // TODO Potentially this should only be done once for all views of an
3932 List<AlcodonFrame> alc = vamsasSet.getAlcodonFrame();
3933 for (int i = 0; i < alc.size(); i++)
3935 AlignedCodonFrame cf = new AlignedCodonFrame();
3936 if (alc.get(i).getAlcodMap().size() > 0)
3938 List<AlcodMap> maps = alc.get(i).getAlcodMap();
3939 for (int m = 0; m < maps.size(); m++)
3941 AlcodMap map = maps.get(m);
3942 SequenceI dnaseq = seqRefIds.get(map.getDnasq());
3944 jalview.datamodel.Mapping mapping = null;
3945 // attach to dna sequence reference.
3946 if (map.getMapping() != null)
3948 mapping = addMapping(map.getMapping());
3949 if (dnaseq != null && mapping.getTo() != null)
3951 cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
3957 .add(newAlcodMapRef(map.getDnasq(), cf, mapping));
3961 al.addCodonFrame(cf);
3966 // ////////////////////////////////
3968 List<JvAnnotRow> autoAlan = new ArrayList<>();
3971 * store any annotations which forward reference a group's ID
3973 Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<>();
3975 if (vamsasSet.getAnnotation().size()/*Count()*/ > 0)
3977 List<Annotation> an = vamsasSet.getAnnotation();
3979 for (int i = 0; i < an.size(); i++)
3981 Annotation annotation = an.get(i);
3984 * test if annotation is automatically calculated for this view only
3986 boolean autoForView = false;
3987 if (annotation.getLabel().equals("Quality")
3988 || annotation.getLabel().equals("Conservation")
3989 || annotation.getLabel().equals("Consensus"))
3991 // Kludge for pre 2.5 projects which lacked the autocalculated flag
3993 // JAXB has no has() test; schema defaults value to false
3994 // if (!annotation.hasAutoCalculated())
3996 // annotation.setAutoCalculated(true);
3999 if (autoForView || annotation.isAutoCalculated())
4001 // remove ID - we don't recover annotation from other views for
4002 // view-specific annotation
4003 annotation.setId(null);
4006 // set visibility for other annotation in this view
4007 String annotationId = annotation.getId();
4008 if (annotationId != null && annotationIds.containsKey(annotationId))
4010 AlignmentAnnotation jda = annotationIds.get(annotationId);
4011 // in principle Visible should always be true for annotation displayed
4012 // in multiple views
4013 if (annotation.isVisible() != null)
4015 jda.visible = annotation.isVisible();
4018 al.addAnnotation(jda);
4022 // Construct new annotation from model.
4023 List<AnnotationElement> ae = annotation.getAnnotationElement();
4024 jalview.datamodel.Annotation[] anot = null;
4025 java.awt.Color firstColour = null;
4027 if (!annotation.isScoreOnly())
4029 anot = new jalview.datamodel.Annotation[al.getWidth()];
4030 for (int aa = 0; aa < ae.size() && aa < anot.length; aa++)
4032 AnnotationElement annElement = ae.get(aa);
4033 anpos = annElement.getPosition();
4035 if (anpos >= anot.length)
4040 float value = safeFloat(annElement.getValue());
4041 anot[anpos] = new jalview.datamodel.Annotation(
4042 annElement.getDisplayCharacter(),
4043 annElement.getDescription(),
4044 (annElement.getSecondaryStructure() == null
4045 || annElement.getSecondaryStructure()
4049 .getSecondaryStructure()
4052 anot[anpos].colour = new Color(safeInt(annElement.getColour()));
4053 if (firstColour == null)
4055 firstColour = anot[anpos].colour;
4059 jalview.datamodel.AlignmentAnnotation jaa = null;
4061 if (annotation.isGraph())
4063 float llim = 0, hlim = 0;
4064 // if (autoForView || an[i].isAutoCalculated()) {
4067 jaa = new jalview.datamodel.AlignmentAnnotation(
4068 annotation.getLabel(), annotation.getDescription(), anot,
4069 llim, hlim, safeInt(annotation.getGraphType()));
4071 jaa.graphGroup = safeInt(annotation.getGraphGroup());
4072 jaa._linecolour = firstColour;
4073 if (annotation.getThresholdLine() != null)
4075 jaa.setThreshold(new jalview.datamodel.GraphLine(
4076 safeFloat(annotation.getThresholdLine().getValue()),
4077 annotation.getThresholdLine().getLabel(),
4078 new java.awt.Color(safeInt(
4079 annotation.getThresholdLine().getColour()))));
4081 if (autoForView || annotation.isAutoCalculated())
4083 // Hardwire the symbol display line to ensure that labels for
4084 // histograms are displayed
4090 jaa = new jalview.datamodel.AlignmentAnnotation(
4091 annotation.getLabel(), annotation.getDescription(), anot);
4092 jaa._linecolour = firstColour;
4094 // register new annotation
4095 if (annotation.getId() != null)
4097 annotationIds.put(annotation.getId(), jaa);
4098 jaa.annotationId = annotation.getId();
4100 // recover sequence association
4101 String sequenceRef = annotation.getSequenceRef();
4102 if (sequenceRef != null)
4104 // from 2.9 sequenceRef is to sequence id (JAL-1781)
4105 SequenceI sequence = seqRefIds.get(sequenceRef);
4106 if (sequence == null)
4108 // in pre-2.9 projects sequence ref is to sequence name
4109 sequence = al.findName(sequenceRef);
4111 if (sequence != null)
4113 jaa.createSequenceMapping(sequence, 1, true);
4114 sequence.addAlignmentAnnotation(jaa);
4117 // and make a note of any group association
4118 if (annotation.getGroupRef() != null
4119 && annotation.getGroupRef().length() > 0)
4121 List<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
4122 .get(annotation.getGroupRef());
4125 aal = new ArrayList<>();
4126 groupAnnotRefs.put(annotation.getGroupRef(), aal);
4131 if (annotation.getScore() != null)
4133 jaa.setScore(annotation.getScore().doubleValue());
4135 if (annotation.isVisible() != null)
4137 jaa.visible = annotation.isVisible().booleanValue();
4140 if (annotation.isCentreColLabels() != null)
4142 jaa.centreColLabels = annotation.isCentreColLabels()
4146 if (annotation.isScaleColLabels() != null)
4148 jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue();
4150 if (annotation.isAutoCalculated())
4152 // newer files have an 'autoCalculated' flag and store calculation
4153 // state in viewport properties
4154 jaa.autoCalculated = true; // means annotation will be marked for
4155 // update at end of load.
4157 if (annotation.getGraphHeight() != null)
4159 jaa.graphHeight = annotation.getGraphHeight().intValue();
4161 jaa.belowAlignment = annotation.isBelowAlignment();
4162 jaa.setCalcId(annotation.getCalcId());
4163 if (annotation.getProperty().size() > 0)
4165 for (jalview.xml.binding.jalview.Property prop : annotation
4168 jaa.setProperty(prop.getName(), prop.getValue());
4171 if (jaa.graph == AlignmentAnnotation.CONTACT_MAP)
4173 if (annotation.getContactmatrix() != null
4174 && annotation.getContactmatrix().size() > 0)
4176 for (MapOnAMatrixType xmlmat : annotation.getContactmatrix())
4178 restoreMatrixFor(jaa.sequenceRef, jaa, xmlmat);
4183 if (jaa.autoCalculated)
4185 autoAlan.add(new JvAnnotRow(i, jaa));
4188 // if (!autoForView)
4190 // add autocalculated group annotation and any user created annotation
4192 al.addAnnotation(jaa);
4196 // ///////////////////////
4198 // Create alignment markup and styles for this view
4199 if (jalviewModel.getJGroup().size() > 0)
4201 List<JGroup> groups = jalviewModel.getJGroup();
4202 boolean addAnnotSchemeGroup = false;
4203 for (int i = 0; i < groups.size(); i++)
4205 JGroup jGroup = groups.get(i);
4206 ColourSchemeI cs = null;
4207 if (jGroup.getColour() != null)
4209 if (jGroup.getColour().startsWith("ucs"))
4211 cs = getUserColourScheme(jalviewModel, jGroup.getColour());
4213 else if (jGroup.getColour().equals("AnnotationColourGradient")
4214 && jGroup.getAnnotationColours() != null)
4216 addAnnotSchemeGroup = true;
4220 cs = ColourSchemeProperty.getColourScheme(null, al,
4221 jGroup.getColour());
4224 int pidThreshold = safeInt(jGroup.getPidThreshold());
4226 Vector<SequenceI> seqs = new Vector<>();
4228 for (int s = 0; s < jGroup.getSeq().size(); s++)
4230 String seqId = jGroup.getSeq().get(s);
4231 SequenceI ts = seqRefIds.get(seqId);
4235 seqs.addElement(ts);
4239 if (seqs.size() < 1)
4244 SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
4245 safeBoolean(jGroup.isDisplayBoxes()),
4246 safeBoolean(jGroup.isDisplayText()),
4247 safeBoolean(jGroup.isColourText()),
4248 safeInt(jGroup.getStart()), safeInt(jGroup.getEnd()));
4249 sg.getGroupColourScheme().setThreshold(pidThreshold, true);
4250 sg.getGroupColourScheme()
4251 .setConservationInc(safeInt(jGroup.getConsThreshold()));
4252 sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour())));
4254 sg.textColour = new Color(safeInt(jGroup.getTextCol1()));
4255 sg.textColour2 = new Color(safeInt(jGroup.getTextCol2()));
4256 sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved()));
4257 sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold());
4258 // attributes with a default in the schema are never null
4259 sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
4260 sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
4261 sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
4262 sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus());
4263 if (jGroup.getConsThreshold() != null
4264 && jGroup.getConsThreshold().intValue() != 0)
4266 Conservation c = new Conservation("All", sg.getSequences(null), 0,
4269 c.verdict(false, 25);
4270 sg.cs.setConservation(c);
4273 if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
4275 // re-instate unique group/annotation row reference
4276 List<AlignmentAnnotation> jaal = groupAnnotRefs
4277 .get(jGroup.getId());
4280 for (AlignmentAnnotation jaa : jaal)
4283 if (jaa.autoCalculated)
4285 // match up and try to set group autocalc alignment row for this
4287 if (jaa.label.startsWith("Consensus for "))
4289 sg.setConsensus(jaa);
4291 // match up and try to set group autocalc alignment row for this
4293 if (jaa.label.startsWith("Conservation for "))
4295 sg.setConservationRow(jaa);
4302 if (addAnnotSchemeGroup)
4304 // reconstruct the annotation colourscheme
4306 constructAnnotationColour(jGroup.getAnnotationColours(),
4307 null, al, jalviewModel, false));
4313 // only dataset in this model, so just return.
4316 // ///////////////////////////////
4319 AlignFrame af = null;
4320 AlignViewport av = null;
4321 // now check to see if we really need to create a new viewport.
4322 if (multipleView && viewportsAdded.size() == 0)
4324 // We recovered an alignment for which a viewport already exists.
4325 // TODO: fix up any settings necessary for overlaying stored state onto
4326 // state recovered from another document. (may not be necessary).
4327 // we may need a binding from a viewport in memory to one recovered from
4329 // and then recover its containing af to allow the settings to be applied.
4330 // TODO: fix for vamsas demo
4331 jalview.bin.Console.errPrintln(
4332 "About to recover a viewport for existing alignment: Sequence set ID is "
4334 Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
4335 if (seqsetobj != null)
4337 if (seqsetobj instanceof String)
4339 uniqueSeqSetId = (String) seqsetobj;
4340 jalview.bin.Console.errPrintln(
4341 "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
4346 jalview.bin.Console.errPrintln(
4347 "Warning : Collision between sequence set ID string and existing jalview object mapping.");
4353 * indicate that annotation colours are applied across all groups (pre
4354 * Jalview 2.8.1 behaviour)
4356 boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
4357 jalviewModel.getVersion());
4359 AlignmentPanel ap = null;
4360 boolean isnewview = true;
4363 // Check to see if this alignment already has a view id == viewId
4364 jalview.gui.AlignmentPanel views[] = Desktop
4365 .getAlignmentPanels(uniqueSeqSetId);
4366 if (views != null && views.length > 0)
4368 for (int v = 0; v < views.length; v++)
4370 if (views[v].av.getViewId().equalsIgnoreCase(viewId))
4372 // recover the existing alignpanel, alignframe, viewport
4373 af = views[v].alignFrame;
4376 // TODO: could even skip resetting view settings if we don't want to
4377 // change the local settings from other jalview processes
4386 af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
4387 uniqueSeqSetId, viewId, autoAlan);
4388 av = af.getViewport();
4393 * Load any trees, PDB structures and viewers, Overview
4395 * Not done if flag is false (when this method is used for New View)
4397 if (loadTreesAndStructures)
4399 loadTrees(jalviewModel, view, af, av, ap);
4400 loadPCAViewers(jalviewModel, ap);
4401 loadPDBStructures(jprovider, jseqs, af, ap);
4402 loadRnaViewers(jprovider, jseqs, ap);
4403 loadOverview(view, jalviewModel.getVersion(), af);
4405 // and finally return.
4409 private void importMatrixData(List<MatrixType> xmlmatrices)
4411 for (MatrixType xmlmat : xmlmatrices)
4413 if (!PAEContactMatrix.PAEMATRIX.equals(xmlmat.getType()))
4415 Console.error("Ignoring matrix '" + xmlmat.getId() + "' of type '"
4416 + xmlmat.getType());
4420 if (!xmlmat.getRows().equals(xmlmat.getCols()))
4422 Console.error("Can't handle non square matrices");
4426 float[][] elements = ContactMatrix.fromFloatStringToContacts(
4427 xmlmat.getElements(), xmlmat.getCols().intValue(),
4428 xmlmat.getRows().intValue());
4430 List<BitSet> newgroups = new ArrayList<BitSet>();
4431 if (xmlmat.getGroups().size() > 0)
4433 for (String sgroup : xmlmat.getGroups())
4435 newgroups.add(deStringifyBitset(sgroup));
4438 String nwk = xmlmat.getNewick().size() > 0 ? xmlmat.getNewick().get(0)
4440 if (xmlmat.getNewick().size() > 1)
4443 .info("Ignoring additional clusterings for contact matrix");
4445 String treeMethod = xmlmat.getTreeMethod();
4446 double thresh = xmlmat.getCutHeight() != null ? xmlmat.getCutHeight()
4448 GroupSet grpset = new GroupSet();
4449 grpset.restoreGroups(newgroups, treeMethod, nwk, thresh);
4451 FloatContactMatrix newcm = new FloatContactMatrix(elements, grpset);
4452 contactMatrixRefs.put(xmlmat.getId(), newcm);
4453 Console.trace("Restored base contact matrix " + xmlmat.getId());
4457 private void restoreMatrixFor(SequenceI sequenceRef,
4458 AlignmentAnnotation jaa, MapOnAMatrixType xmlmatmapping)
4460 // restore mapping data to matrix data
4461 jalview.util.MapList mapping = null;
4462 if (xmlmatmapping.getMapping() != null)
4464 MapListType m = xmlmatmapping.getMapping();
4465 // Mapping m = dr.getMapping();
4466 int fr[] = new int[m.getMapListFrom().size() * 2];
4467 Iterator<MapListFrom> from = m.getMapListFrom().iterator();// enumerateMapListFrom();
4468 for (int _i = 0; from.hasNext(); _i += 2)
4470 MapListFrom mf = from.next();
4471 fr[_i] = mf.getStart();
4472 fr[_i + 1] = mf.getEnd();
4474 int fto[] = new int[m.getMapListTo().size() * 2];
4475 Iterator<MapListTo> to = m.getMapListTo().iterator();// enumerateMapListTo();
4476 for (int _i = 0; to.hasNext(); _i += 2)
4478 MapListTo mf = to.next();
4479 fto[_i] = mf.getStart();
4480 fto[_i + 1] = mf.getEnd();
4483 mapping = new jalview.util.MapList(fr, fto,
4484 m.getMapFromUnit().intValue(), m.getMapToUnit().intValue());
4487 // locate matrix data in project XML and import
4488 ContactMatrixI cm = contactMatrixRefs.get(xmlmatmapping.getMatrix());
4492 .add(newMatrixFref(xmlmatmapping.getMatrix(), mapping, jaa));
4496 // create the PAEMatrix now
4497 PAEContactMatrix newpae = new PAEContactMatrix(jaa.sequenceRef,
4500 jaa.sequenceRef.addContactListFor(jaa, newpae);
4507 * Load Overview window, restoring colours, 'show hidden regions' flag, title
4508 * and geometry as saved
4513 protected void loadOverview(Viewport view, String version, AlignFrame af)
4515 if (!isVersionStringLaterThan("2.11.3", version)
4516 && view.getOverview() == null)
4521 * first close any Overview that was opened automatically
4522 * (if so configured in Preferences) so that the view is
4523 * restored in the same state as saved
4525 af.alignPanel.closeOverviewPanel();
4527 Overview overview = view.getOverview();
4528 if (overview != null)
4530 OverviewPanel overviewPanel = af
4531 .openOverviewPanel(overview.isShowHidden());
4532 overviewPanel.setTitle(overview.getTitle());
4533 overviewPanel.setFrameBounds(overview.getXpos(), overview.getYpos(),
4534 overview.getWidth(), overview.getHeight());
4535 Color gap = new Color(overview.getGapColour());
4536 Color residue = new Color(overview.getResidueColour());
4537 Color hidden = new Color(overview.getHiddenColour());
4538 overviewPanel.getCanvas().setColours(gap, residue, hidden);
4543 * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna
4544 * panel is restored from separate jar entries, two (gapped and trimmed) per
4545 * sequence and secondary structure.
4547 * Currently each viewer shows just one sequence and structure (gapped and
4548 * trimmed), however this method is designed to support multiple sequences or
4549 * structures in viewers if wanted in future.
4555 private void loadRnaViewers(jarInputStreamProvider jprovider,
4556 List<JSeq> jseqs, AlignmentPanel ap)
4559 * scan the sequences for references to viewers; create each one the first
4560 * time it is referenced, add Rna models to existing viewers
4562 for (JSeq jseq : jseqs)
4564 for (int i = 0; i < jseq.getRnaViewer().size(); i++)
4566 RnaViewer viewer = jseq.getRnaViewer().get(i);
4567 AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
4570 for (int j = 0; j < viewer.getSecondaryStructure().size(); j++)
4572 SecondaryStructure ss = viewer.getSecondaryStructure().get(j);
4573 SequenceI seq = seqRefIds.get(jseq.getId());
4574 AlignmentAnnotation ann = this.annotationIds
4575 .get(ss.getAnnotationId());
4578 * add the structure to the Varna display (with session state copied
4579 * from the jar to a temporary file)
4581 boolean gapped = safeBoolean(ss.isGapped());
4582 String rnaTitle = ss.getTitle();
4583 String sessionState = ss.getViewerState();
4584 String tempStateFile = copyJarEntry(jprovider, sessionState,
4586 RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
4587 appVarna.addModelSession(rna, rnaTitle, tempStateFile);
4589 appVarna.setInitialSelection(safeInt(viewer.getSelectedRna()));
4595 * Locate and return an already instantiated matching AppVarna, or create one
4599 * @param viewIdSuffix
4603 protected AppVarna findOrCreateVarnaViewer(RnaViewer viewer,
4604 String viewIdSuffix, AlignmentPanel ap)
4607 * on each load a suffix is appended to the saved viewId, to avoid conflicts
4608 * if load is repeated
4610 String postLoadId = viewer.getViewId() + viewIdSuffix;
4611 for (JInternalFrame frame : getAllFrames())
4613 if (frame instanceof AppVarna)
4615 AppVarna varna = (AppVarna) frame;
4616 if (postLoadId.equals(varna.getViewId()))
4618 // this viewer is already instantiated
4619 // could in future here add ap as another 'parent' of the
4620 // AppVarna window; currently just 1-to-many
4627 * viewer not found - make it
4629 RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(),
4630 safeInt(viewer.getXpos()), safeInt(viewer.getYpos()),
4631 safeInt(viewer.getWidth()), safeInt(viewer.getHeight()),
4632 safeInt(viewer.getDividerLocation()));
4633 AppVarna varna = new AppVarna(model, ap);
4639 * Load any saved trees
4647 protected void loadTrees(JalviewModel jm, Viewport view, AlignFrame af,
4648 AlignViewport av, AlignmentPanel ap)
4650 // TODO result of automated refactoring - are all these parameters needed?
4653 for (int t = 0; t < jm.getTree().size(); t++)
4656 Tree tree = jm.getTree().get(t);
4658 TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
4661 if (tree.isColumnWise())
4663 AlignmentAnnotation aa = annotationIds
4664 .get(tree.getColumnReference());
4668 "Null alignment annotation when restoring columnwise tree");
4670 tp = af.showColumnWiseTree(new NewickFile(tree.getNewick()), aa,
4671 tree.getTitle(), safeInt(tree.getWidth()),
4672 safeInt(tree.getHeight()), safeInt(tree.getXpos()),
4673 safeInt(tree.getYpos()));
4678 tp = af.showNewickTree(new NewickFile(tree.getNewick()),
4679 tree.getTitle(), safeInt(tree.getWidth()),
4680 safeInt(tree.getHeight()), safeInt(tree.getXpos()),
4681 safeInt(tree.getYpos()));
4683 if (tree.getId() != null)
4685 // perhaps bind the tree id to something ?
4690 // update local tree attributes ?
4691 // TODO: should check if tp has been manipulated by user - if so its
4692 // settings shouldn't be modified
4693 tp.setTitle(tree.getTitle());
4694 tp.setBounds(new Rectangle(safeInt(tree.getXpos()),
4695 safeInt(tree.getYpos()), safeInt(tree.getWidth()),
4696 safeInt(tree.getHeight())));
4697 tp.setViewport(av); // af.viewport;
4698 // TODO: verify 'associate with all views' works still
4699 tp.getTreeCanvas().setViewport(av); // af.viewport;
4700 tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
4702 tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
4706 "There was a problem recovering stored Newick tree: \n"
4707 + tree.getNewick());
4711 tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow()));
4712 tp.fitToWindow_actionPerformed(null);
4714 if (tree.getFontName() != null)
4717 new Font(tree.getFontName(), safeInt(tree.getFontStyle()),
4718 safeInt(tree.getFontSize())));
4723 new Font(view.getFontName(), safeInt(view.getFontStyle()),
4724 safeInt(view.getFontSize())));
4727 tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked()));
4728 tp.showBootstrap(safeBoolean(tree.isShowBootstrap()));
4729 tp.showDistances(safeBoolean(tree.isShowDistances()));
4731 tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold()));
4733 if (safeBoolean(tree.isCurrentTree()))
4735 af.getViewport().setCurrentTree(tp.getTree());
4739 } catch (Exception ex)
4741 ex.printStackTrace();
4746 * Load and link any saved structure viewers.
4753 protected void loadPDBStructures(jarInputStreamProvider jprovider,
4754 List<JSeq> jseqs, AlignFrame af, AlignmentPanel ap)
4757 * Run through all PDB ids on the alignment, and collect mappings between
4758 * distinct view ids and all sequences referring to that view.
4760 Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<>();
4762 for (int i = 0; i < jseqs.size(); i++)
4764 JSeq jseq = jseqs.get(i);
4765 if (jseq.getPdbids().size() > 0)
4767 List<Pdbids> ids = jseq.getPdbids();
4768 for (int p = 0; p < ids.size(); p++)
4770 Pdbids pdbid = ids.get(p);
4771 final int structureStateCount = pdbid.getStructureState().size();
4772 for (int s = 0; s < structureStateCount; s++)
4774 // check to see if we haven't already created this structure view
4775 final StructureState structureState = pdbid.getStructureState()
4777 String sviewid = (structureState.getViewId() == null) ? null
4778 : structureState.getViewId() + uniqueSetSuffix;
4779 jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
4780 // Originally : pdbid.getFile()
4781 // : TODO: verify external PDB file recovery still works in normal
4782 // jalview project load
4784 loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile()));
4785 jpdb.setId(pdbid.getId());
4787 int x = safeInt(structureState.getXpos());
4788 int y = safeInt(structureState.getYpos());
4789 int width = safeInt(structureState.getWidth());
4790 int height = safeInt(structureState.getHeight());
4792 // Probably don't need to do this anymore...
4793 // Desktop.desktop.getComponentAt(x, y);
4794 // TODO: NOW: check that this recovers the PDB file correctly.
4795 String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
4797 jalview.datamodel.SequenceI seq = seqRefIds
4798 .get(jseq.getId() + "");
4799 if (sviewid == null)
4801 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + ","
4804 if (!structureViewers.containsKey(sviewid))
4806 String viewerType = structureState.getType();
4807 if (viewerType == null) // pre Jalview 2.9
4809 viewerType = ViewerType.JMOL.toString();
4811 structureViewers.put(sviewid,
4812 new StructureViewerModel(x, y, width, height, false,
4813 false, true, structureState.getViewId(),
4815 // Legacy pre-2.7 conversion JAL-823 :
4816 // do not assume any view has to be linked for colour by
4820 // assemble String[] { pdb files }, String[] { id for each
4821 // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
4822 // seqs_file 2}, boolean[] {
4823 // linkAlignPanel,superposeWithAlignpanel}} from hash
4824 StructureViewerModel jmoldat = structureViewers.get(sviewid);
4825 jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
4826 || structureState.isAlignwithAlignPanel());
4829 * Default colour by linked panel to false if not specified (e.g.
4830 * for pre-2.7 projects)
4832 boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
4833 colourWithAlignPanel |= structureState.isColourwithAlignPanel();
4834 jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
4837 * Default colour by viewer to true if not specified (e.g. for
4840 boolean colourByViewer = jmoldat.isColourByViewer();
4841 colourByViewer &= structureState.isColourByJmol();
4842 jmoldat.setColourByViewer(colourByViewer);
4844 if (jmoldat.getStateData().length() < structureState.getValue()
4845 /*Content()*/.length())
4847 jmoldat.setStateData(structureState.getValue());// Content());
4849 if (pdbid.getFile() != null)
4851 File mapkey = new File(pdbid.getFile());
4852 StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
4853 if (seqstrmaps == null)
4855 jmoldat.getFileData().put(mapkey,
4856 seqstrmaps = jmoldat.new StructureData(pdbFile,
4859 if (!seqstrmaps.getSeqList().contains(seq))
4861 seqstrmaps.getSeqList().add(seq);
4867 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");
4868 Console.warn(errorMessage);
4874 // Instantiate the associated structure views
4875 for (Entry<String, StructureViewerModel> entry : structureViewers
4880 createOrLinkStructureViewer(entry, af, ap, jprovider);
4881 } catch (Exception e)
4883 jalview.bin.Console.errPrintln(
4884 "Error loading structure viewer: " + e.getMessage());
4885 // failed - try the next one
4897 protected void createOrLinkStructureViewer(
4898 Entry<String, StructureViewerModel> viewerData, AlignFrame af,
4899 AlignmentPanel ap, jarInputStreamProvider jprovider)
4901 final StructureViewerModel stateData = viewerData.getValue();
4904 * Search for any viewer windows already open from other alignment views
4905 * that exactly match the stored structure state
4907 StructureViewerBase comp = findMatchingViewer(viewerData);
4911 linkStructureViewer(ap, comp, stateData);
4915 String type = stateData.getType();
4918 ViewerType viewerType = ViewerType.valueOf(type);
4919 createStructureViewer(viewerType, viewerData, af, jprovider);
4920 } catch (IllegalArgumentException | NullPointerException e)
4922 // TODO JAL-3619 show error dialog / offer an alternative viewer
4923 Console.error("Invalid structure viewer type: " + type);
4928 * Generates a name for the entry in the project jar file to hold state
4929 * information for a structure viewer
4934 protected String getViewerJarEntryName(String viewId)
4936 return VIEWER_PREFIX + viewId;
4940 * Returns any open frame that matches given structure viewer data. The match
4941 * is based on the unique viewId, or (for older project versions) the frame's
4947 protected StructureViewerBase findMatchingViewer(
4948 Entry<String, StructureViewerModel> viewerData)
4950 final String sviewid = viewerData.getKey();
4951 final StructureViewerModel svattrib = viewerData.getValue();
4952 StructureViewerBase comp = null;
4953 JInternalFrame[] frames = getAllFrames();
4954 for (JInternalFrame frame : frames)
4956 if (frame instanceof StructureViewerBase)
4959 * Post jalview 2.4 schema includes structure view id
4961 if (sviewid != null && ((StructureViewerBase) frame).getViewId()
4964 comp = (StructureViewerBase) frame;
4965 break; // break added in 2.9
4968 * Otherwise test for matching position and size of viewer frame
4970 else if (frame.getX() == svattrib.getX()
4971 && frame.getY() == svattrib.getY()
4972 && frame.getHeight() == svattrib.getHeight()
4973 && frame.getWidth() == svattrib.getWidth())
4975 comp = (StructureViewerBase) frame;
4976 // no break in faint hope of an exact match on viewId
4984 * Link an AlignmentPanel to an existing structure viewer.
4989 * @param useinViewerSuperpos
4990 * @param usetoColourbyseq
4991 * @param viewerColouring
4993 protected void linkStructureViewer(AlignmentPanel ap,
4994 StructureViewerBase viewer, StructureViewerModel stateData)
4996 // NOTE: if the jalview project is part of a shared session then
4997 // view synchronization should/could be done here.
4999 final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
5000 final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
5001 final boolean viewerColouring = stateData.isColourByViewer();
5002 Map<File, StructureData> oldFiles = stateData.getFileData();
5005 * Add mapping for sequences in this view to an already open viewer
5007 final AAStructureBindingModel binding = viewer.getBinding();
5008 for (File id : oldFiles.keySet())
5010 // add this and any other pdb files that should be present in the
5012 StructureData filedat = oldFiles.get(id);
5013 String pdbFile = filedat.getFilePath();
5014 SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
5015 binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
5017 binding.addSequenceForStructFile(pdbFile, seq);
5019 // and add the AlignmentPanel's reference to the view panel
5020 viewer.addAlignmentPanel(ap);
5021 if (useinViewerSuperpos)
5023 viewer.useAlignmentPanelForSuperposition(ap);
5027 viewer.excludeAlignmentPanelForSuperposition(ap);
5029 if (usetoColourbyseq)
5031 viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
5035 viewer.excludeAlignmentPanelForColourbyseq(ap);
5040 * Get all frames within the Desktop.
5044 protected JInternalFrame[] getAllFrames()
5046 JInternalFrame[] frames = null;
5047 // TODO is this necessary - is it safe - risk of hanging?
5052 frames = Desktop.desktop.getAllFrames();
5053 } catch (ArrayIndexOutOfBoundsException e)
5055 // occasional No such child exceptions are thrown here...
5059 } catch (InterruptedException f)
5063 } while (frames == null);
5068 * Answers true if 'version' is equal to or later than 'supported', where each
5069 * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
5070 * changes. Development and test values for 'version' are leniently treated
5074 * - minimum version we are comparing against
5076 * - version of data being processsed
5077 * @return true if version is equal to or later than supported
5079 public static boolean isVersionStringLaterThan(String supported,
5082 if (supported == null || version == null
5083 || version.equalsIgnoreCase("DEVELOPMENT BUILD")
5084 || version.equalsIgnoreCase("Test")
5085 || version.equalsIgnoreCase("AUTOMATED BUILD"))
5087 jalview.bin.Console.errPrintln("Assuming project file with "
5088 + (version == null ? "null" : version)
5089 + " is compatible with Jalview version " + supported);
5094 return StringUtils.compareVersions(version, supported, "b") >= 0;
5098 Vector<JalviewStructureDisplayI> newStructureViewers = null;
5100 protected void addNewStructureViewer(JalviewStructureDisplayI sview)
5102 if (newStructureViewers != null)
5104 sview.getBinding().setFinishedLoadingFromArchive(false);
5105 newStructureViewers.add(sview);
5109 protected void setLoadingFinishedForNewStructureViewers()
5111 if (newStructureViewers != null)
5113 for (JalviewStructureDisplayI sview : newStructureViewers)
5115 sview.getBinding().setFinishedLoadingFromArchive(true);
5117 newStructureViewers.clear();
5118 newStructureViewers = null;
5122 AlignFrame loadViewport(String file, List<JSeq> JSEQ,
5123 List<SequenceI> hiddenSeqs, AlignmentI al, JalviewModel jm,
5124 Viewport view, String uniqueSeqSetId, String viewId,
5125 List<JvAnnotRow> autoAlan)
5127 AlignFrame af = null;
5128 af = new AlignFrame(al, safeInt(view.getWidth()),
5129 safeInt(view.getHeight()), uniqueSeqSetId, viewId)
5133 // protected void processKeyEvent(java.awt.event.KeyEvent e) {
5134 // jalview.bin.Console.outPrintln("Jalview2XML AF " + e);
5135 // super.processKeyEvent(e);
5142 af.setFileName(file, FileFormat.Jalview);
5144 final AlignViewport viewport = af.getViewport();
5145 for (int i = 0; i < JSEQ.size(); i++)
5147 int colour = safeInt(JSEQ.get(i).getColour());
5148 viewport.setSequenceColour(viewport.getAlignment().getSequenceAt(i),
5154 viewport.setColourByReferenceSeq(true);
5155 viewport.setDisplayReferenceSeq(true);
5158 viewport.setGatherViewsHere(safeBoolean(view.isGatheredViews()));
5160 if (view.getSequenceSetId() != null)
5162 AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId);
5164 viewport.setSequenceSetId(uniqueSeqSetId);
5167 // propagate shared settings to this new view
5168 viewport.setHistoryList(av.getHistoryList());
5169 viewport.setRedoList(av.getRedoList());
5173 viewportsAdded.put(uniqueSeqSetId, viewport);
5175 // TODO: check if this method can be called repeatedly without
5176 // side-effects if alignpanel already registered.
5177 PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
5179 // apply Hidden regions to view.
5180 if (hiddenSeqs != null)
5182 for (int s = 0; s < JSEQ.size(); s++)
5184 SequenceGroup hidden = new SequenceGroup();
5185 boolean isRepresentative = false;
5186 for (int r = 0; r < JSEQ.get(s).getHiddenSequences().size(); r++)
5188 isRepresentative = true;
5189 SequenceI sequenceToHide = al
5190 .getSequenceAt(JSEQ.get(s).getHiddenSequences().get(r));
5191 hidden.addSequence(sequenceToHide, false);
5192 // remove from hiddenSeqs list so we don't try to hide it twice
5193 hiddenSeqs.remove(sequenceToHide);
5195 if (isRepresentative)
5197 SequenceI representativeSequence = al.getSequenceAt(s);
5198 hidden.addSequence(representativeSequence, false);
5199 viewport.hideRepSequences(representativeSequence, hidden);
5203 SequenceI[] hseqs = hiddenSeqs
5204 .toArray(new SequenceI[hiddenSeqs.size()]);
5205 viewport.hideSequence(hseqs);
5208 // recover view properties and display parameters
5210 viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation()));
5211 viewport.setAbovePIDThreshold(safeBoolean(view.isPidSelected()));
5212 final int pidThreshold = safeInt(view.getPidThreshold());
5213 viewport.setThreshold(pidThreshold);
5215 viewport.setColourText(safeBoolean(view.isShowColourText()));
5217 viewport.setConservationSelected(
5218 safeBoolean(view.isConservationSelected()));
5219 viewport.setIncrement(safeInt(view.getConsThreshold()));
5220 viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
5221 viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
5223 new Font(view.getFontName(), safeInt(view.getFontStyle()),
5224 safeInt(view.getFontSize())),
5225 (view.getCharWidth() != null) ? false : true);
5226 if (view.getCharWidth() != null)
5228 viewport.setCharWidth(view.getCharWidth());
5229 viewport.setCharHeight(view.getCharHeight());
5231 ViewStyleI vs = viewport.getViewStyle();
5232 vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna());
5233 viewport.setViewStyle(vs);
5234 // TODO: allow custom charWidth/Heights to be restored by updating them
5235 // after setting font - which means set above to false
5236 viewport.setRenderGaps(safeBoolean(view.isRenderGaps()));
5237 viewport.setWrapAlignment(safeBoolean(view.isWrapAlignment()));
5238 viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation()));
5240 viewport.setShowBoxes(safeBoolean(view.isShowBoxes()));
5242 viewport.setShowText(safeBoolean(view.isShowText()));
5244 viewport.setTextColour(new Color(safeInt(view.getTextCol1())));
5245 viewport.setTextColour2(new Color(safeInt(view.getTextCol2())));
5246 viewport.setThresholdTextColour(safeInt(view.getTextColThreshold()));
5247 viewport.setShowUnconserved(view.isShowUnconserved());
5248 viewport.getRanges().setStartRes(safeInt(view.getStartRes()));
5250 if (view.getViewName() != null)
5252 viewport.setViewName(view.getViewName());
5253 af.setInitialTabVisible();
5255 af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
5256 safeInt(view.getWidth()), safeInt(view.getHeight()));
5258 af.alignPanel.fontChanged(); // make sure font is updated *before* we set ID
5260 if (view.getIdWidth() == null)
5262 if (!isVersionStringLaterThan("2.11.3", jm.getVersion()))
5264 // Pre 2.11.3 jalview projects do not store the id width
5265 // idWidth was also calculated in a different way.
5266 viewport.setIdWidth(af.alignPanel.getLegacyIdWidth());
5267 af.alignPanel.getIdPanel().getIdCanvas().setManuallyAdjusted(true);
5272 viewport.setIdWidth(view.getIdWidth());
5273 af.alignPanel.getIdPanel().getIdCanvas()
5274 .setManuallyAdjusted(view.isIdWidthManuallyAdjusted());
5277 // startSeq set in af.alignPanel.updateLayout below
5278 af.alignPanel.updateLayout();
5279 ColourSchemeI cs = null;
5280 // apply colourschemes
5281 if (view.getBgColour() != null)
5283 if (view.getBgColour().startsWith("ucs"))
5285 cs = getUserColourScheme(jm, view.getBgColour());
5287 else if (view.getBgColour().startsWith("Annotation"))
5289 AnnotationColourScheme viewAnnColour = view.getAnnotationColours();
5290 cs = constructAnnotationColour(viewAnnColour, af, al, jm, true);
5297 cs = ColourSchemeProperty.getColourScheme(af.getViewport(), al,
5298 view.getBgColour());
5303 * turn off 'alignment colour applies to all groups'
5304 * while restoring global colour scheme
5306 viewport.setColourAppliesToAllGroups(false);
5307 viewport.setGlobalColourScheme(cs);
5308 viewport.getResidueShading().setThreshold(pidThreshold,
5309 view.isIgnoreGapsinConsensus());
5310 viewport.getResidueShading()
5311 .setConsensus(viewport.getSequenceConsensusHash());
5312 viewport.getResidueShading()
5313 .setSsConsensus(viewport.getSequenceSSConsensusHash());
5314 if (safeBoolean(view.isConservationSelected()) && cs != null)
5316 viewport.getResidueShading()
5317 .setConservationInc(safeInt(view.getConsThreshold()));
5319 af.changeColour(cs);
5320 viewport.setColourAppliesToAllGroups(true);
5322 viewport.setShowSequenceFeatures(
5323 safeBoolean(view.isShowSequenceFeatures()));
5325 viewport.setCentreColumnLabels(view.isCentreColumnLabels());
5326 viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null);
5327 viewport.setFollowHighlight(view.isFollowHighlight());
5328 viewport.followSelection = view.isFollowSelection();
5329 viewport.setShowConsensusHistogram(view.isShowConsensusHistogram());
5330 viewport.setShowSequenceLogo(view.isShowSequenceLogo());
5331 viewport.setNormaliseSequenceLogo(view.isNormaliseSequenceLogo());
5332 viewport.setShowDBRefs(safeBoolean(view.isShowDbRefTooltip()));
5333 viewport.setShowNPFeats(safeBoolean(view.isShowNPfeatureTooltip()));
5334 viewport.setShowGroupConsensus(view.isShowGroupConsensus());
5335 viewport.setShowGroupConservation(view.isShowGroupConservation());
5336 viewport.setShowComplementFeatures(view.isShowComplementFeatures());
5337 viewport.setShowComplementFeaturesOnTop(
5338 view.isShowComplementFeaturesOnTop());
5340 // recover feature settings
5341 if (jm.getFeatureSettings() != null)
5343 FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas
5344 .getFeatureRenderer();
5345 FeaturesDisplayed fdi;
5346 viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
5347 String[] renderOrder = new String[jm.getFeatureSettings().getSetting()
5349 Map<String, FeatureColourI> featureColours = new Hashtable<>();
5350 Map<String, Float> featureOrder = new Hashtable<>();
5352 for (int fs = 0; fs < jm.getFeatureSettings().getSetting()
5355 Setting setting = jm.getFeatureSettings().getSetting().get(fs);
5356 String featureType = setting.getType();
5359 * restore feature filters (if any)
5361 jalview.xml.binding.jalview.FeatureMatcherSet filters = setting
5363 if (filters != null)
5365 FeatureMatcherSetI filter = Jalview2XML.parseFilter(featureType,
5367 if (!filter.isEmpty())
5369 fr.setFeatureFilter(featureType, filter);
5374 * restore feature colour scheme
5376 Color maxColour = new Color(setting.getColour());
5377 if (setting.getMincolour() != null)
5380 * minColour is always set unless a simple colour
5381 * (including for colour by label though it doesn't use it)
5383 Color minColour = new Color(setting.getMincolour().intValue());
5384 Color noValueColour = minColour;
5385 NoValueColour noColour = setting.getNoValueColour();
5386 if (noColour == NoValueColour.NONE)
5388 noValueColour = null;
5390 else if (noColour == NoValueColour.MAX)
5392 noValueColour = maxColour;
5394 float min = safeFloat(safeFloat(setting.getMin()));
5395 float max = setting.getMax() == null ? 1f
5396 : setting.getMax().floatValue();
5397 FeatureColourI gc = new FeatureColour(maxColour, minColour,
5398 maxColour, noValueColour, min, max);
5399 if (setting.getAttributeName().size() > 0)
5401 gc.setAttributeName(setting.getAttributeName().toArray(
5402 new String[setting.getAttributeName().size()]));
5404 if (setting.getThreshold() != null)
5406 gc.setThreshold(setting.getThreshold().floatValue());
5407 int threshstate = safeInt(setting.getThreshstate());
5408 // -1 = None, 0 = Below, 1 = Above threshold
5409 if (threshstate == 0)
5411 gc.setBelowThreshold(true);
5413 else if (threshstate == 1)
5415 gc.setAboveThreshold(true);
5418 gc.setAutoScaled(true); // default
5419 if (setting.isAutoScale() != null)
5421 gc.setAutoScaled(setting.isAutoScale());
5423 if (setting.isColourByLabel() != null)
5425 gc.setColourByLabel(setting.isColourByLabel());
5427 // and put in the feature colour table.
5428 featureColours.put(featureType, gc);
5432 featureColours.put(featureType, new FeatureColour(maxColour));
5434 renderOrder[fs] = featureType;
5435 if (setting.getOrder() != null)
5437 featureOrder.put(featureType, setting.getOrder().floatValue());
5441 featureOrder.put(featureType, Float.valueOf(
5442 fs / jm.getFeatureSettings().getSetting().size()));
5444 if (safeBoolean(setting.isDisplay()))
5446 fdi.setVisible(featureType);
5449 Map<String, Boolean> fgtable = new Hashtable<>();
5450 for (int gs = 0; gs < jm.getFeatureSettings().getGroup().size(); gs++)
5452 Group grp = jm.getFeatureSettings().getGroup().get(gs);
5453 fgtable.put(grp.getName(), Boolean.valueOf(grp.isDisplay()));
5455 // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
5456 // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
5457 // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
5458 FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
5459 fgtable, featureColours, 1.0f, featureOrder);
5460 fr.transferSettings(frs);
5463 if (view.getHiddenColumns().size() > 0)
5465 for (int c = 0; c < view.getHiddenColumns().size(); c++)
5467 final HiddenColumns hc = view.getHiddenColumns().get(c);
5468 viewport.hideColumns(safeInt(hc.getStart()),
5469 safeInt(hc.getEnd()) /* +1 */);
5472 if (view.getCalcIdParam() != null)
5474 for (CalcIdParam calcIdParam : view.getCalcIdParam())
5476 if (calcIdParam != null)
5478 if (recoverCalcIdParam(calcIdParam, viewport))
5483 Console.warn("Couldn't recover parameters for "
5484 + calcIdParam.getCalcId());
5489 af.setMenusFromViewport(viewport);
5490 af.setTitle(view.getTitle());
5491 // TODO: we don't need to do this if the viewport is aready visible.
5493 * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
5494 * has a 'cdna/protein complement' view, in which case save it in order to
5495 * populate a SplitFrame once all views have been read in.
5497 String complementaryViewId = view.getComplementId();
5498 if (complementaryViewId == null)
5500 Desktop.addInternalFrame(af, view.getTitle(),
5501 safeInt(view.getWidth()), safeInt(view.getHeight()));
5502 // recompute any autoannotation
5503 af.alignPanel.updateAnnotation(false, true);
5504 reorderAutoannotation(af, al, autoAlan);
5505 af.alignPanel.alignmentChanged();
5509 splitFrameCandidates.put(view, af);
5516 * Reads saved data to restore Colour by Annotation settings
5518 * @param viewAnnColour
5522 * @param checkGroupAnnColour
5525 private ColourSchemeI constructAnnotationColour(
5526 AnnotationColourScheme viewAnnColour, AlignFrame af,
5527 AlignmentI al, JalviewModel model, boolean checkGroupAnnColour)
5529 boolean propagateAnnColour = false;
5530 AlignmentI annAlignment = af != null ? af.getViewport().getAlignment()
5532 if (checkGroupAnnColour && al.getGroups() != null
5533 && al.getGroups().size() > 0)
5535 // pre 2.8.1 behaviour
5536 // check to see if we should transfer annotation colours
5537 propagateAnnColour = true;
5538 for (SequenceGroup sg : al.getGroups())
5540 if (sg.getColourScheme() instanceof AnnotationColourGradient)
5542 propagateAnnColour = false;
5548 * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId
5550 String annotationId = viewAnnColour.getAnnotation();
5551 AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId);
5554 * pre 2.10.2: saved annotationId is AlignmentAnnotation.label
5556 if (matchedAnnotation == null
5557 && annAlignment.getAlignmentAnnotation() != null)
5559 for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
5562 .equals(annAlignment.getAlignmentAnnotation()[i].label))
5564 matchedAnnotation = annAlignment.getAlignmentAnnotation()[i];
5569 if (matchedAnnotation == null)
5572 .errPrintln("Failed to match annotation colour scheme for "
5576 // belt-and-braces create a threshold line if the
5577 // colourscheme needs one but the matchedAnnotation doesn't have one
5578 if (safeInt(viewAnnColour.getAboveThreshold()) != 0
5579 && matchedAnnotation.getThreshold() == null)
5581 matchedAnnotation.setThreshold(
5582 new GraphLine(safeFloat(viewAnnColour.getThreshold()),
5583 "Threshold", Color.black));
5586 AnnotationColourGradient cs = null;
5587 if (viewAnnColour.getColourScheme().equals("None"))
5589 cs = new AnnotationColourGradient(matchedAnnotation,
5590 new Color(safeInt(viewAnnColour.getMinColour())),
5591 new Color(safeInt(viewAnnColour.getMaxColour())),
5592 safeInt(viewAnnColour.getAboveThreshold()));
5594 else if (viewAnnColour.getColourScheme().startsWith("ucs"))
5596 cs = new AnnotationColourGradient(matchedAnnotation,
5597 getUserColourScheme(model, viewAnnColour.getColourScheme()),
5598 safeInt(viewAnnColour.getAboveThreshold()));
5602 cs = new AnnotationColourGradient(matchedAnnotation,
5603 ColourSchemeProperty.getColourScheme(af.getViewport(), al,
5604 viewAnnColour.getColourScheme()),
5605 safeInt(viewAnnColour.getAboveThreshold()));
5608 boolean perSequenceOnly = safeBoolean(viewAnnColour.isPerSequence());
5609 boolean useOriginalColours = safeBoolean(
5610 viewAnnColour.isPredefinedColours());
5611 cs.setSeqAssociated(perSequenceOnly);
5612 cs.setPredefinedColours(useOriginalColours);
5614 if (propagateAnnColour && al.getGroups() != null)
5616 // Also use these settings for all the groups
5617 for (int g = 0; g < al.getGroups().size(); g++)
5619 SequenceGroup sg = al.getGroups().get(g);
5620 if (sg.getGroupColourScheme() == null)
5625 AnnotationColourGradient groupScheme = new AnnotationColourGradient(
5626 matchedAnnotation, sg.getColourScheme(),
5627 safeInt(viewAnnColour.getAboveThreshold()));
5628 sg.setColourScheme(groupScheme);
5629 groupScheme.setSeqAssociated(perSequenceOnly);
5630 groupScheme.setPredefinedColours(useOriginalColours);
5636 private void reorderAutoannotation(AlignFrame af, AlignmentI al,
5637 List<JvAnnotRow> autoAlan)
5639 // copy over visualization settings for autocalculated annotation in the
5641 if (al.getAlignmentAnnotation() != null)
5644 * Kludge for magic autoannotation names (see JAL-811)
5646 String[] magicNames = new String[] { "Consensus", "Quality",
5648 JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
5649 Hashtable<String, JvAnnotRow> visan = new Hashtable<>();
5650 for (String nm : magicNames)
5652 visan.put(nm, nullAnnot);
5654 for (JvAnnotRow auan : autoAlan)
5656 visan.put(auan.template.label
5657 + (auan.template.getCalcId() == null ? ""
5658 : "\t" + auan.template.getCalcId()),
5661 int hSize = al.getAlignmentAnnotation().length;
5662 List<JvAnnotRow> reorder = new ArrayList<>();
5663 // work through any autoCalculated annotation already on the view
5664 // removing it if it should be placed in a different location on the
5665 // annotation panel.
5666 List<String> remains = new ArrayList<>(visan.keySet());
5667 for (int h = 0; h < hSize; h++)
5669 jalview.datamodel.AlignmentAnnotation jalan = al
5670 .getAlignmentAnnotation()[h];
5671 if (jalan.autoCalculated)
5674 JvAnnotRow valan = visan.get(k = jalan.label);
5675 if (jalan.getCalcId() != null)
5677 valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
5682 // delete the auto calculated row from the alignment
5683 al.deleteAnnotation(jalan, false);
5687 if (valan != nullAnnot)
5689 if (jalan != valan.template)
5691 // newly created autoannotation row instance
5692 // so keep a reference to the visible annotation row
5693 // and copy over all relevant attributes
5694 if (valan.template.graphHeight >= 0)
5697 jalan.graphHeight = valan.template.graphHeight;
5699 jalan.visible = valan.template.visible;
5701 reorder.add(new JvAnnotRow(valan.order, jalan));
5706 // Add any (possibly stale) autocalculated rows that were not appended to
5707 // the view during construction
5708 for (String other : remains)
5710 JvAnnotRow othera = visan.get(other);
5711 if (othera != nullAnnot && othera.template.getCalcId() != null
5712 && othera.template.getCalcId().length() > 0)
5714 reorder.add(othera);
5717 // now put the automatic annotation in its correct place
5718 int s = 0, srt[] = new int[reorder.size()];
5719 JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
5720 for (JvAnnotRow jvar : reorder)
5723 srt[s++] = jvar.order;
5726 jalview.util.QuickSort.sort(srt, rws);
5727 // and re-insert the annotation at its correct position
5728 for (JvAnnotRow jvar : rws)
5730 al.addAnnotation(jvar.template, jvar.order);
5732 af.alignPanel.adjustAnnotationHeight();
5736 Hashtable skipList = null;
5739 * TODO remove this method
5742 * @return AlignFrame bound to sequenceSetId from view, if one exists. private
5743 * AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
5744 * throw new Error("Implementation Error. No skipList defined for this
5745 * Jalview2XML instance."); } return (AlignFrame)
5746 * skipList.get(view.getSequenceSetId()); }
5750 * Check if the Jalview view contained in object should be skipped or not.
5753 * @return true if view's sequenceSetId is a key in skipList
5755 private boolean skipViewport(JalviewModel object)
5757 if (skipList == null)
5761 String id = object.getViewport().get(0).getSequenceSetId();
5762 if (skipList.containsKey(id))
5764 Console.debug("Skipping seuqence set id " + id);
5770 public void addToSkipList(AlignFrame af)
5772 if (skipList == null)
5774 skipList = new Hashtable();
5776 skipList.put(af.getViewport().getSequenceSetId(), af);
5779 public void clearSkipList()
5781 if (skipList != null)
5788 private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
5789 boolean ignoreUnrefed, String uniqueSeqSetId)
5791 jalview.datamodel.AlignmentI ds = getDatasetFor(
5792 vamsasSet.getDatasetId());
5793 AlignmentI xtant_ds = ds;
5794 if (xtant_ds == null)
5796 // good chance we are about to create a new dataset, but check if we've
5797 // seen some of the dataset sequence IDs before.
5798 // TODO: skip this check if we are working with project generated by
5799 // version 2.11 or later
5800 xtant_ds = checkIfHasDataset(vamsasSet.getSequence());
5801 if (xtant_ds != null)
5804 addDatasetRef(vamsasSet.getDatasetId(), ds);
5807 Vector<SequenceI> dseqs = null;
5810 // recovering an alignment View
5811 AlignmentI seqSetDS = getDatasetFor(UNIQSEQSETID + uniqueSeqSetId);
5812 if (seqSetDS != null)
5814 if (ds != null && ds != seqSetDS)
5817 "JAL-3171 regression: Overwriting a dataset reference for an alignment"
5818 + " - CDS/Protein crossreference data may be lost");
5819 if (xtant_ds != null)
5821 // This can only happen if the unique sequence set ID was bound to a
5822 // dataset that did not contain any of the sequences in the view
5823 // currently being restored.
5825 "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.");
5829 addDatasetRef(vamsasSet.getDatasetId(), ds);
5834 // try even harder to restore dataset
5835 AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence());
5836 // create a list of new dataset sequences
5837 dseqs = new Vector<>();
5839 for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++)
5841 Sequence vamsasSeq = vamsasSet.getSequence().get(i);
5842 ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i);
5844 // create a new dataset
5847 SequenceI[] dsseqs = new SequenceI[dseqs.size()];
5848 dseqs.copyInto(dsseqs);
5849 ds = new jalview.datamodel.Alignment(dsseqs);
5850 Console.debug("Created new dataset " + vamsasSet.getDatasetId()
5851 + " for alignment " + System.identityHashCode(al));
5852 addDatasetRef(vamsasSet.getDatasetId(), ds);
5854 // set the dataset for the newly imported alignment.
5855 if (al.getDataset() == null && !ignoreUnrefed)
5858 // register dataset for the alignment's uniqueSeqSetId for legacy projects
5859 addDatasetRef(UNIQSEQSETID + uniqueSeqSetId, ds);
5861 updateSeqDatasetBinding(vamsasSet.getSequence(), ds);
5865 * XML dataset sequence ID to materialised dataset reference
5867 HashMap<String, AlignmentI> seqToDataset = new HashMap<>();
5870 * @return the first materialised dataset reference containing a dataset
5871 * sequence referenced in the given view
5873 * - sequences from the view
5875 AlignmentI checkIfHasDataset(List<Sequence> list)
5877 for (Sequence restoredSeq : list)
5879 AlignmentI datasetFor = seqToDataset.get(restoredSeq.getDsseqid());
5880 if (datasetFor != null)
5889 * Register ds as the containing dataset for the dataset sequences referenced
5890 * by sequences in list
5893 * - sequences in a view
5896 void updateSeqDatasetBinding(List<Sequence> list, AlignmentI ds)
5898 for (Sequence restoredSeq : list)
5900 AlignmentI prevDS = seqToDataset.put(restoredSeq.getDsseqid(), ds);
5901 if (prevDS != null && prevDS != ds)
5903 Console.warn("Dataset sequence appears in many datasets: "
5904 + restoredSeq.getDsseqid());
5905 // TODO: try to merge!
5913 * sequence definition to create/merge dataset sequence for
5917 * vector to add new dataset sequence to
5918 * @param ignoreUnrefed
5919 * - when true, don't create new sequences from vamsasSeq if it's id
5920 * doesn't already have an asssociated Jalview sequence.
5922 * - used to reorder the sequence in the alignment according to the
5923 * vamsasSeq array ordering, to preserve ordering of dataset
5925 private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
5926 AlignmentI ds, Vector<SequenceI> dseqs, boolean ignoreUnrefed,
5929 // JBP TODO: Check this is called for AlCodonFrames to support recovery of
5931 SequenceI sq = seqRefIds.get(vamsasSeq.getId());
5932 boolean reorder = false;
5933 SequenceI dsq = null;
5934 if (sq != null && sq.getDatasetSequence() != null)
5936 dsq = sq.getDatasetSequence();
5942 if (sq == null && ignoreUnrefed)
5946 String sqid = vamsasSeq.getDsseqid();
5949 // need to create or add a new dataset sequence reference to this sequence
5952 dsq = seqRefIds.get(sqid);
5957 // make a new dataset sequence
5958 dsq = sq.createDatasetSequence();
5961 // make up a new dataset reference for this sequence
5962 sqid = seqHash(dsq);
5964 dsq.setVamsasId(uniqueSetSuffix + sqid);
5965 seqRefIds.put(sqid, dsq);
5970 dseqs.addElement(dsq);
5975 ds.addSequence(dsq);
5981 { // make this dataset sequence sq's dataset sequence
5982 sq.setDatasetSequence(dsq);
5983 // and update the current dataset alignment
5988 if (!dseqs.contains(dsq))
5995 if (ds.findIndex(dsq) < 0)
5997 ds.addSequence(dsq);
6004 // TODO: refactor this as a merge dataset sequence function
6005 // now check that sq (the dataset sequence) sequence really is the union of
6006 // all references to it
6007 // boolean pre = sq.getStart() < dsq.getStart();
6008 // boolean post = sq.getEnd() > dsq.getEnd();
6012 // StringBuffer sb = new StringBuffer();
6013 String newres = jalview.analysis.AlignSeq.extractGaps(
6014 jalview.util.Comparison.GapChars, sq.getSequenceAsString());
6015 if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
6016 && newres.length() > dsq.getLength())
6018 // Update with the longer sequence.
6022 * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
6023 * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
6024 * sb.append(newres.substring(newres.length() - sq.getEnd() -
6025 * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
6027 dsq.setSequence(newres);
6029 // TODO: merges will never happen if we 'know' we have the real dataset
6030 // sequence - this should be detected when id==dssid
6031 jalview.bin.Console.errPrintln(
6032 "DEBUG Notice: Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
6033 // + (pre ? "prepended" : "") + " "
6034 // + (post ? "appended" : ""));
6039 // sequence refs are identical. We may need to update the existing dataset
6040 // alignment with this one, though.
6041 if (ds != null && dseqs == null)
6043 int opos = ds.findIndex(dsq);
6044 SequenceI tseq = null;
6045 if (opos != -1 && vseqpos != opos)
6047 // remove from old position
6048 ds.deleteSequence(dsq);
6050 if (vseqpos < ds.getHeight())
6052 if (vseqpos != opos)
6054 // save sequence at destination position
6055 tseq = ds.getSequenceAt(vseqpos);
6056 ds.replaceSequenceAt(vseqpos, dsq);
6057 ds.addSequence(tseq);
6062 ds.addSequence(dsq);
6069 * TODO use AlignmentI here and in related methods - needs
6070 * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment
6072 Hashtable<String, AlignmentI> datasetIds = null;
6074 IdentityHashMap<AlignmentI, String> dataset2Ids = null;
6076 private AlignmentI getDatasetFor(String datasetId)
6078 if (datasetIds == null)
6080 datasetIds = new Hashtable<>();
6083 if (datasetIds.containsKey(datasetId))
6085 return datasetIds.get(datasetId);
6090 private void addDatasetRef(String datasetId, AlignmentI dataset)
6092 if (datasetIds == null)
6094 datasetIds = new Hashtable<>();
6096 datasetIds.put(datasetId, dataset);
6100 * make a new dataset ID for this jalview dataset alignment
6105 private String getDatasetIdRef(AlignmentI dataset)
6107 if (dataset.getDataset() != null)
6110 "Serious issue! Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
6112 String datasetId = makeHashCode(dataset, null);
6113 if (datasetId == null)
6115 // make a new datasetId and record it
6116 if (dataset2Ids == null)
6118 dataset2Ids = new IdentityHashMap<>();
6122 datasetId = dataset2Ids.get(dataset);
6124 if (datasetId == null)
6126 datasetId = "ds" + dataset2Ids.size() + 1;
6127 dataset2Ids.put(dataset, datasetId);
6134 * Add any saved DBRefEntry's to the sequence. An entry flagged as 'locus' is
6135 * constructed as a special subclass GeneLocus.
6137 * @param datasetSequence
6140 private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
6142 for (int d = 0; d < sequence.getDBRef().size(); d++)
6144 DBRef dr = sequence.getDBRef().get(d);
6148 entry = new GeneLocus(dr.getSource(), dr.getVersion(),
6149 dr.getAccessionId());
6153 entry = new DBRefEntry(dr.getSource(), dr.getVersion(),
6154 dr.getAccessionId());
6156 if (dr.getMapping() != null)
6158 entry.setMap(addMapping(dr.getMapping()));
6160 entry.setCanonical(dr.isCanonical());
6161 datasetSequence.addDBRef(entry);
6165 private jalview.datamodel.Mapping addMapping(Mapping m)
6167 SequenceI dsto = null;
6168 // Mapping m = dr.getMapping();
6169 int fr[] = new int[m.getMapListFrom().size() * 2];
6170 Iterator<MapListFrom> from = m.getMapListFrom().iterator();// enumerateMapListFrom();
6171 for (int _i = 0; from.hasNext(); _i += 2)
6173 MapListFrom mf = from.next();
6174 fr[_i] = mf.getStart();
6175 fr[_i + 1] = mf.getEnd();
6177 int fto[] = new int[m.getMapListTo().size() * 2];
6178 Iterator<MapListTo> to = m.getMapListTo().iterator();// enumerateMapListTo();
6179 for (int _i = 0; to.hasNext(); _i += 2)
6181 MapListTo mf = to.next();
6182 fto[_i] = mf.getStart();
6183 fto[_i + 1] = mf.getEnd();
6185 jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto, fr,
6186 fto, m.getMapFromUnit().intValue(),
6187 m.getMapToUnit().intValue());
6190 * (optional) choice of dseqFor or Sequence
6192 if (m.getDseqFor() != null)
6194 String dsfor = m.getDseqFor();
6195 if (seqRefIds.containsKey(dsfor))
6200 jmap.setTo(seqRefIds.get(dsfor));
6204 frefedSequence.add(newMappingRef(dsfor, jmap));
6207 else if (m.getSequence() != null)
6210 * local sequence definition
6212 Sequence ms = m.getSequence();
6213 SequenceI djs = null;
6214 String sqid = ms.getDsseqid();
6215 if (sqid != null && sqid.length() > 0)
6218 * recover dataset sequence
6220 djs = seqRefIds.get(sqid);
6224 jalview.bin.Console.errPrintln(
6225 "Warning - making up dataset sequence id for DbRef sequence map reference");
6226 sqid = ((Object) ms).toString(); // make up a new hascode for
6227 // undefined dataset sequence hash
6228 // (unlikely to happen)
6234 * make a new dataset sequence and add it to refIds hash
6236 djs = new jalview.datamodel.Sequence(ms.getName(),
6238 djs.setStart(jmap.getMap().getToLowest());
6239 djs.setEnd(jmap.getMap().getToHighest());
6240 djs.setVamsasId(uniqueSetSuffix + sqid);
6242 incompleteSeqs.put(sqid, djs);
6243 seqRefIds.put(sqid, djs);
6246 Console.debug("about to recurse on addDBRefs.");
6255 * Provides a 'copy' of an alignment view (on action New View) by 'saving' the
6256 * view as XML (but not to file), and then reloading it
6261 public AlignmentPanel copyAlignPanel(AlignmentPanel ap)
6264 JalviewModel jm = saveState(ap, null, null, null);
6267 jm.getVamsasModel().getSequenceSet().get(0).getDatasetId(),
6268 ap.getAlignment().getDataset());
6270 uniqueSetSuffix = "";
6271 // jm.getJalviewModelSequence().getViewport(0).setId(null);
6272 jm.getViewport().get(0).setId(null);
6273 // we don't overwrite the view we just copied
6275 if (this.frefedSequence == null)
6277 frefedSequence = new Vector<>();
6280 viewportsAdded.clear();
6282 AlignFrame af = loadFromObject(jm, null, false, null);
6283 af.getAlignPanels().clear();
6284 af.closeMenuItem_actionPerformed(true);
6287 * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
6288 * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
6289 * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
6290 * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
6291 * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
6294 return af.alignPanel;
6297 private Hashtable jvids2vobj;
6300 * set the object to ID mapping tables used to write/recover objects and XML
6301 * ID strings for the jalview project. If external tables are provided then
6302 * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
6303 * object goes out of scope. - also populates the datasetIds hashtable with
6304 * alignment objects containing dataset sequences
6307 * Map from ID strings to jalview datamodel
6309 * Map from jalview datamodel to ID strings
6313 public void setObjectMappingTables(Hashtable vobj2jv,
6314 IdentityHashMap jv2vobj)
6316 this.jv2vobj = jv2vobj;
6317 this.vobj2jv = vobj2jv;
6318 Iterator ds = jv2vobj.keySet().iterator();
6320 while (ds.hasNext())
6322 Object jvobj = ds.next();
6323 id = jv2vobj.get(jvobj).toString();
6324 if (jvobj instanceof jalview.datamodel.Alignment)
6326 if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
6328 addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
6331 else if (jvobj instanceof jalview.datamodel.Sequence)
6333 // register sequence object so the XML parser can recover it.
6334 if (seqRefIds == null)
6336 seqRefIds = new HashMap<>();
6338 if (seqsToIds == null)
6340 seqsToIds = new IdentityHashMap<>();
6342 seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
6343 seqsToIds.put((SequenceI) jvobj, id);
6345 else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
6348 AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj;
6349 annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann);
6350 if (jvann.annotationId == null)
6352 jvann.annotationId = anid;
6354 if (!jvann.annotationId.equals(anid))
6356 // TODO verify that this is the correct behaviour
6357 Console.warn("Overriding Annotation ID for " + anid
6358 + " from different id : " + jvann.annotationId);
6359 jvann.annotationId = anid;
6362 else if (jvobj instanceof String)
6364 if (jvids2vobj == null)
6366 jvids2vobj = new Hashtable();
6367 jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
6372 Console.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
6378 * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
6379 * objects created from the project archive. If string is null (default for
6380 * construction) then suffix will be set automatically.
6384 public void setUniqueSetSuffix(String string)
6386 uniqueSetSuffix = string;
6391 * uses skipList2 as the skipList for skipping views on sequence sets
6392 * associated with keys in the skipList
6396 public void setSkipList(Hashtable skipList2)
6398 skipList = skipList2;
6402 * Reads the jar entry of given name and returns its contents, or null if the
6403 * entry is not found.
6406 * @param jarEntryName
6409 protected String readJarEntry(jarInputStreamProvider jprovider,
6410 String jarEntryName)
6412 String result = null;
6413 BufferedReader in = null;
6418 * Reopen the jar input stream and traverse its entries to find a matching
6421 JarInputStream jin = jprovider.getJarInputStream();
6422 JarEntry entry = null;
6425 entry = jin.getNextJarEntry();
6426 } while (entry != null && !entry.getName().equals(jarEntryName));
6430 StringBuilder out = new StringBuilder(256);
6431 in = new BufferedReader(new InputStreamReader(jin, UTF_8));
6434 while ((data = in.readLine()) != null)
6438 result = out.toString();
6443 "Couldn't find entry in Jalview Jar for " + jarEntryName);
6445 } catch (Exception ex)
6447 ex.printStackTrace();
6455 } catch (IOException e)
6466 * Returns an incrementing counter (0, 1, 2...)
6470 private synchronized int nextCounter()
6476 * Loads any saved PCA viewers
6481 protected void loadPCAViewers(JalviewModel model, AlignmentPanel ap)
6485 List<PcaViewer> pcaviewers = model.getPcaViewer();
6486 for (PcaViewer viewer : pcaviewers)
6488 String modelName = viewer.getScoreModelName();
6489 SimilarityParamsI params = new SimilarityParams(
6490 viewer.isIncludeGappedColumns(), viewer.isMatchGaps(),
6491 viewer.isIncludeGaps(),
6492 viewer.isDenominateByShortestLength());
6495 * create the panel (without computing the PCA)
6497 PCAPanel panel = new PCAPanel(ap, modelName, params);
6499 panel.setTitle(viewer.getTitle());
6500 panel.setBounds(new Rectangle(viewer.getXpos(), viewer.getYpos(),
6501 viewer.getWidth(), viewer.getHeight()));
6503 boolean showLabels = viewer.isShowLabels();
6504 panel.setShowLabels(showLabels);
6505 panel.getRotatableCanvas().setShowLabels(showLabels);
6506 panel.getRotatableCanvas()
6507 .setBgColour(new Color(viewer.getBgColour()));
6508 panel.getRotatableCanvas()
6509 .setApplyToAllViews(viewer.isLinkToAllViews());
6512 * load PCA output data
6514 ScoreModelI scoreModel = ScoreModels.getInstance()
6515 .getScoreModel(modelName, ap);
6516 PCA pca = new PCA(null, scoreModel, params);
6517 PcaDataType pcaData = viewer.getPcaData();
6519 MatrixI pairwise = loadDoubleMatrix(pcaData.getPairwiseMatrix());
6520 pca.setPairwiseScores(pairwise);
6522 MatrixI triDiag = loadDoubleMatrix(pcaData.getTridiagonalMatrix());
6523 pca.setTridiagonal(triDiag);
6525 MatrixI result = loadDoubleMatrix(pcaData.getEigenMatrix());
6526 pca.setEigenmatrix(result);
6528 panel.getPcaModel().setPCA(pca);
6531 * we haven't saved the input data! (JAL-2647 to do)
6533 panel.setInputData(null);
6536 * add the sequence points for the PCA display
6538 List<jalview.datamodel.SequencePoint> seqPoints = new ArrayList<>();
6539 for (SequencePoint sp : viewer.getSequencePoint())
6541 String seqId = sp.getSequenceRef();
6542 SequenceI seq = seqRefIds.get(seqId);
6545 throw new IllegalStateException(
6546 "Unmatched seqref for PCA: " + seqId);
6548 Point pt = new Point(sp.getXPos(), sp.getYPos(), sp.getZPos());
6549 jalview.datamodel.SequencePoint seqPoint = new jalview.datamodel.SequencePoint(
6551 seqPoints.add(seqPoint);
6553 panel.getRotatableCanvas().setPoints(seqPoints, seqPoints.size());
6556 * set min-max ranges and scale after setPoints (which recomputes them)
6558 panel.getRotatableCanvas().setScaleFactor(viewer.getScaleFactor());
6559 SeqPointMin spMin = viewer.getSeqPointMin();
6560 float[] min = new float[] { spMin.getXPos(), spMin.getYPos(),
6562 SeqPointMax spMax = viewer.getSeqPointMax();
6563 float[] max = new float[] { spMax.getXPos(), spMax.getYPos(),
6565 panel.getRotatableCanvas().setSeqMinMax(min, max);
6567 // todo: hold points list in PCAModel only
6568 panel.getPcaModel().setSequencePoints(seqPoints);
6570 panel.setSelectedDimensionIndex(viewer.getXDim(), X);
6571 panel.setSelectedDimensionIndex(viewer.getYDim(), Y);
6572 panel.setSelectedDimensionIndex(viewer.getZDim(), Z);
6574 // is this duplication needed?
6575 panel.setTop(seqPoints.size() - 1);
6576 panel.getPcaModel().setTop(seqPoints.size() - 1);
6579 * add the axes' end points for the display
6581 for (int i = 0; i < 3; i++)
6583 Axis axis = viewer.getAxis().get(i);
6584 panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point(
6585 axis.getXPos(), axis.getYPos(), axis.getZPos());
6588 Desktop.addInternalFrame(panel, MessageManager.formatMessage(
6589 "label.calc_title", "PCA", modelName), 475, 450);
6591 } catch (Exception ex)
6593 Console.error("Error loading PCA: " + ex.toString());
6598 * Creates a new structure viewer window
6605 protected void createStructureViewer(ViewerType viewerType,
6606 final Entry<String, StructureViewerModel> viewerData,
6607 AlignFrame af, jarInputStreamProvider jprovider)
6609 final StructureViewerModel viewerModel = viewerData.getValue();
6610 String sessionFilePath = null;
6612 if (viewerType == ViewerType.JMOL)
6614 sessionFilePath = rewriteJmolSession(viewerModel, jprovider);
6618 String viewerJarEntryName = getViewerJarEntryName(
6619 viewerModel.getViewId());
6620 sessionFilePath = copyJarEntry(jprovider, viewerJarEntryName,
6621 "viewerSession", ".tmp");
6623 final String sessionPath = sessionFilePath;
6624 final String sviewid = viewerData.getKey();
6627 SwingUtilities.invokeAndWait(new Runnable()
6632 JalviewStructureDisplayI sview = null;
6635 sview = StructureViewer.createView(viewerType, af.alignPanel,
6636 viewerModel, sessionPath, sviewid);
6637 addNewStructureViewer(sview);
6638 } catch (OutOfMemoryError ex)
6640 new OOMWarning("Restoring structure view for " + viewerType,
6641 (OutOfMemoryError) ex.getCause());
6642 if (sview != null && sview.isVisible())
6644 sview.closeViewer(false);
6645 sview.setVisible(false);
6651 } catch (InvocationTargetException | InterruptedException ex)
6653 Console.warn("Unexpected error when opening " + viewerType
6654 + " structure viewer", ex);
6659 * Rewrites a Jmol session script, saves it to a temporary file, and returns
6660 * the path of the file. "load file" commands are rewritten to change the
6661 * original PDB file names to those created as the Jalview project is loaded.
6667 private String rewriteJmolSession(StructureViewerModel svattrib,
6668 jarInputStreamProvider jprovider)
6670 String state = svattrib.getStateData(); // Jalview < 2.9
6671 if (state == null || state.isEmpty()) // Jalview >= 2.9
6673 String jarEntryName = getViewerJarEntryName(svattrib.getViewId());
6674 state = readJarEntry(jprovider, jarEntryName);
6676 // TODO or simpler? for each key in oldFiles,
6677 // replace key.getPath() in state with oldFiles.get(key).getFilePath()
6678 // (allowing for different path escapings)
6679 StringBuilder rewritten = new StringBuilder(state.length());
6680 int cp = 0, ncp, ecp;
6681 Map<File, StructureData> oldFiles = svattrib.getFileData();
6682 while ((ncp = state.indexOf("load ", cp)) > -1)
6686 // look for next filename in load statement
6687 rewritten.append(state.substring(cp,
6688 ncp = (state.indexOf("\"", ncp + 1) + 1)));
6689 String oldfilenam = state.substring(ncp,
6690 ecp = state.indexOf("\"", ncp));
6691 // recover the new mapping data for this old filename
6692 // have to normalize filename - since Jmol and jalview do
6693 // filename translation differently.
6694 StructureData filedat = oldFiles.get(new File(oldfilenam));
6695 if (filedat == null)
6697 String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
6698 filedat = oldFiles.get(new File(reformatedOldFilename));
6700 rewritten.append(Platform.escapeBackslashes(filedat.getFilePath()));
6701 rewritten.append("\"");
6702 cp = ecp + 1; // advance beyond last \" and set cursor so we can
6703 // look for next file statement.
6704 } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
6708 // just append rest of state
6709 rewritten.append(state.substring(cp));
6713 System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
6714 rewritten = new StringBuilder(state);
6715 rewritten.append("; load append ");
6716 for (File id : oldFiles.keySet())
6718 // add pdb files that should be present in the viewer
6719 StructureData filedat = oldFiles.get(id);
6720 rewritten.append(" \"").append(filedat.getFilePath()).append("\"");
6722 rewritten.append(";");
6725 if (rewritten.length() == 0)
6729 final String history = "history = ";
6730 int historyIndex = rewritten.indexOf(history);
6731 if (historyIndex > -1)
6734 * change "history = [true|false];" to "history = [1|0];"
6736 historyIndex += history.length();
6737 String val = rewritten.substring(historyIndex, historyIndex + 5);
6738 if (val.startsWith("true"))
6740 rewritten.replace(historyIndex, historyIndex + 4, "1");
6742 else if (val.startsWith("false"))
6744 rewritten.replace(historyIndex, historyIndex + 5, "0");
6750 File tmp = File.createTempFile("viewerSession", ".tmp");
6751 try (OutputStream os = new FileOutputStream(tmp))
6753 InputStream is = new ByteArrayInputStream(
6754 rewritten.toString().getBytes());
6756 return tmp.getAbsolutePath();
6758 } catch (IOException e)
6760 Console.error("Error restoring Jmol session: " + e.toString());
6766 * Populates an XML model of the feature colour scheme for one feature type
6768 * @param featureType
6772 public static Colour marshalColour(String featureType,
6773 FeatureColourI fcol)
6775 Colour col = new Colour();
6776 if (fcol.isSimpleColour())
6778 col.setRGB(Format.getHexString(fcol.getColour()));
6782 col.setRGB(Format.getHexString(fcol.getMaxColour()));
6783 col.setMin(fcol.getMin());
6784 col.setMax(fcol.getMax());
6785 col.setMinRGB(jalview.util.Format.getHexString(fcol.getMinColour()));
6786 col.setAutoScale(fcol.isAutoScaled());
6787 col.setThreshold(fcol.getThreshold());
6788 col.setColourByLabel(fcol.isColourByLabel());
6789 col.setThreshType(fcol.isAboveThreshold() ? ThresholdType.ABOVE
6790 : (fcol.isBelowThreshold() ? ThresholdType.BELOW
6791 : ThresholdType.NONE));
6792 if (fcol.isColourByAttribute())
6794 final String[] attName = fcol.getAttributeName();
6795 col.getAttributeName().add(attName[0]);
6796 if (attName.length > 1)
6798 col.getAttributeName().add(attName[1]);
6801 Color noColour = fcol.getNoColour();
6802 if (noColour == null)
6804 col.setNoValueColour(NoValueColour.NONE);
6806 else if (noColour == fcol.getMaxColour())
6808 col.setNoValueColour(NoValueColour.MAX);
6812 col.setNoValueColour(NoValueColour.MIN);
6815 col.setName(featureType);
6820 * Populates an XML model of the feature filter(s) for one feature type
6822 * @param firstMatcher
6823 * the first (or only) match condition)
6825 * remaining match conditions (if any)
6827 * if true, conditions are and-ed, else or-ed
6829 public static jalview.xml.binding.jalview.FeatureMatcherSet marshalFilter(
6830 FeatureMatcherI firstMatcher, Iterator<FeatureMatcherI> filters,
6833 jalview.xml.binding.jalview.FeatureMatcherSet result = new jalview.xml.binding.jalview.FeatureMatcherSet();
6835 if (filters.hasNext())
6840 CompoundMatcher compound = new CompoundMatcher();
6841 compound.setAnd(and);
6842 jalview.xml.binding.jalview.FeatureMatcherSet matcher1 = marshalFilter(
6843 firstMatcher, Collections.emptyIterator(), and);
6844 // compound.addMatcherSet(matcher1);
6845 compound.getMatcherSet().add(matcher1);
6846 FeatureMatcherI nextMatcher = filters.next();
6847 jalview.xml.binding.jalview.FeatureMatcherSet matcher2 = marshalFilter(
6848 nextMatcher, filters, and);
6849 // compound.addMatcherSet(matcher2);
6850 compound.getMatcherSet().add(matcher2);
6851 result.setCompoundMatcher(compound);
6856 * single condition matcher
6858 // MatchCondition matcherModel = new MatchCondition();
6859 jalview.xml.binding.jalview.FeatureMatcher matcherModel = new jalview.xml.binding.jalview.FeatureMatcher();
6860 matcherModel.setCondition(
6861 firstMatcher.getMatcher().getCondition().getStableName());
6862 matcherModel.setValue(firstMatcher.getMatcher().getPattern());
6863 if (firstMatcher.isByAttribute())
6865 matcherModel.setBy(FilterBy.BY_ATTRIBUTE);
6866 // matcherModel.setAttributeName(firstMatcher.getAttribute());
6867 String[] attName = firstMatcher.getAttribute();
6868 matcherModel.getAttributeName().add(attName[0]); // attribute
6869 if (attName.length > 1)
6871 matcherModel.getAttributeName().add(attName[1]); // sub-attribute
6874 else if (firstMatcher.isByLabel())
6876 matcherModel.setBy(FilterBy.BY_LABEL);
6878 else if (firstMatcher.isByScore())
6880 matcherModel.setBy(FilterBy.BY_SCORE);
6882 result.setMatchCondition(matcherModel);
6889 * Loads one XML model of a feature filter to a Jalview object
6891 * @param featureType
6892 * @param matcherSetModel
6895 public static FeatureMatcherSetI parseFilter(String featureType,
6896 jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel)
6898 FeatureMatcherSetI result = new FeatureMatcherSet();
6901 parseFilterConditions(result, matcherSetModel, true);
6902 } catch (IllegalStateException e)
6904 // mixing AND and OR conditions perhaps
6905 jalview.bin.Console.errPrintln(
6906 String.format("Error reading filter conditions for '%s': %s",
6907 featureType, e.getMessage()));
6908 // return as much as was parsed up to the error
6915 * Adds feature match conditions to matcherSet as unmarshalled from XML
6916 * (possibly recursively for compound conditions)
6919 * @param matcherSetModel
6921 * if true, multiple conditions are AND-ed, else they are OR-ed
6922 * @throws IllegalStateException
6923 * if AND and OR conditions are mixed
6925 protected static void parseFilterConditions(FeatureMatcherSetI matcherSet,
6926 jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel,
6929 jalview.xml.binding.jalview.FeatureMatcher mc = matcherSetModel
6930 .getMatchCondition();
6936 FilterBy filterBy = mc.getBy();
6937 Condition cond = Condition.fromString(mc.getCondition());
6938 String pattern = mc.getValue();
6939 FeatureMatcherI matchCondition = null;
6940 if (filterBy == FilterBy.BY_LABEL)
6942 matchCondition = FeatureMatcher.byLabel(cond, pattern);
6944 else if (filterBy == FilterBy.BY_SCORE)
6946 matchCondition = FeatureMatcher.byScore(cond, pattern);
6949 else if (filterBy == FilterBy.BY_ATTRIBUTE)
6951 final List<String> attributeName = mc.getAttributeName();
6952 String[] attNames = attributeName
6953 .toArray(new String[attributeName.size()]);
6954 matchCondition = FeatureMatcher.byAttribute(cond, pattern,
6959 * note this throws IllegalStateException if AND-ing to a
6960 * previously OR-ed compound condition, or vice versa
6964 matcherSet.and(matchCondition);
6968 matcherSet.or(matchCondition);
6974 * compound condition
6976 List<jalview.xml.binding.jalview.FeatureMatcherSet> matchers = matcherSetModel
6977 .getCompoundMatcher().getMatcherSet();
6978 boolean anded = matcherSetModel.getCompoundMatcher().isAnd();
6979 if (matchers.size() == 2)
6981 parseFilterConditions(matcherSet, matchers.get(0), anded);
6982 parseFilterConditions(matcherSet, matchers.get(1), anded);
6987 .errPrintln("Malformed compound filter condition");
6993 * Loads one XML model of a feature colour to a Jalview object
6995 * @param colourModel
6998 public static FeatureColourI parseColour(Colour colourModel)
7000 FeatureColourI colour = null;
7002 if (colourModel.getMax() != null)
7004 Color mincol = null;
7005 Color maxcol = null;
7006 Color noValueColour = null;
7010 mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16));
7011 maxcol = new Color(Integer.parseInt(colourModel.getRGB(), 16));
7012 } catch (Exception e)
7014 Console.warn("Couldn't parse out graduated feature color.", e);
7017 NoValueColour noCol = colourModel.getNoValueColour();
7018 if (noCol == NoValueColour.MIN)
7020 noValueColour = mincol;
7022 else if (noCol == NoValueColour.MAX)
7024 noValueColour = maxcol;
7027 colour = new FeatureColour(maxcol, mincol, maxcol, noValueColour,
7028 safeFloat(colourModel.getMin()),
7029 safeFloat(colourModel.getMax()));
7030 final List<String> attributeName = colourModel.getAttributeName();
7031 String[] attributes = attributeName
7032 .toArray(new String[attributeName.size()]);
7033 if (attributes != null && attributes.length > 0)
7035 colour.setAttributeName(attributes);
7037 if (colourModel.isAutoScale() != null)
7039 colour.setAutoScaled(colourModel.isAutoScale().booleanValue());
7041 if (colourModel.isColourByLabel() != null)
7043 colour.setColourByLabel(
7044 colourModel.isColourByLabel().booleanValue());
7046 if (colourModel.getThreshold() != null)
7048 colour.setThreshold(colourModel.getThreshold().floatValue());
7050 ThresholdType ttyp = colourModel.getThreshType();
7051 if (ttyp == ThresholdType.ABOVE)
7053 colour.setAboveThreshold(true);
7055 else if (ttyp == ThresholdType.BELOW)
7057 colour.setBelowThreshold(true);
7062 Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16));
7063 colour = new FeatureColour(color);
7069 public static void setStateSavedUpToDate(boolean s)
7071 Console.debug("Setting overall stateSavedUpToDate to " + s);
7072 stateSavedUpToDate = s;
7075 public static boolean stateSavedUpToDate()
7077 Console.debug("Returning overall stateSavedUpToDate value: "
7078 + stateSavedUpToDate);
7079 return stateSavedUpToDate;
7082 public static boolean allSavedUpToDate()
7084 if (stateSavedUpToDate()) // nothing happened since last project save
7087 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
7090 for (int i = 0; i < frames.length; i++)
7092 if (frames[i] == null)
7094 if (!frames[i].getViewport().savedUpToDate())
7095 return false; // at least one alignment is not individually saved
7101 // used for debugging and tests
7102 private static int debugDelaySave = 20;
7104 public static void setDebugDelaySave(int n)