X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fproject%2FJalview2XML.java;h=16c324d64bc5267483be134b49e5266c135c4ccb;hb=47d13cdd39a8ce2f59adda6bc9b79374dd49be56;hp=c4ad897d4a04f9769d849454461ca7b1d1e8748c;hpb=0c805692348a8a45c279ee5f4702c7bd5a393c5e;p=jalview.git diff --git a/src/jalview/project/Jalview2XML.java b/src/jalview/project/Jalview2XML.java index c4ad897..16c324d 100644 --- a/src/jalview/project/Jalview2XML.java +++ b/src/jalview/project/Jalview2XML.java @@ -20,17 +20,79 @@ */ package jalview.project; +import static jalview.math.RotatableMatrix.Axis.X; +import static jalview.math.RotatableMatrix.Axis.Y; +import static jalview.math.RotatableMatrix.Axis.Z; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Rectangle; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; + +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.Marshaller; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + import jalview.analysis.Conservation; +import jalview.analysis.PCA; +import jalview.analysis.scoremodels.ScoreModels; +import jalview.analysis.scoremodels.SimilarityParams; import jalview.api.FeatureColourI; import jalview.api.ViewStyleI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.GeneLocus; import jalview.datamodel.GraphLine; import jalview.datamodel.PDBEntry; +import jalview.datamodel.Point; import jalview.datamodel.RnaViewerModel; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; @@ -46,20 +108,23 @@ import jalview.gui.AlignFrame; import jalview.gui.AlignViewport; import jalview.gui.AlignmentPanel; import jalview.gui.AppVarna; -import jalview.gui.ChimeraViewFrame; import jalview.gui.Desktop; -import jalview.gui.FeatureRenderer; -import jalview.gui.Jalview2XML_V1; import jalview.gui.JvOptionPane; import jalview.gui.OOMWarning; +import jalview.gui.OverviewPanel; +import jalview.gui.PCAPanel; import jalview.gui.PaintRefresher; import jalview.gui.SplitFrame; import jalview.gui.StructureViewer; import jalview.gui.StructureViewer.ViewerType; import jalview.gui.StructureViewerBase; import jalview.gui.TreePanel; +import jalview.io.BackupFiles; import jalview.io.DataSourceType; import jalview.io.FileFormat; +import jalview.io.NewickFile; +import jalview.math.Matrix; +import jalview.math.MatrixI; import jalview.renderer.ResidueShaderI; import jalview.schemes.AnnotationColourGradient; import jalview.schemes.ColourSchemeI; @@ -76,7 +141,9 @@ import jalview.util.StringUtils; import jalview.util.jarInputStreamProvider; import jalview.util.matcher.Condition; import jalview.viewmodel.AlignmentViewport; +import jalview.viewmodel.PCAModel; import jalview.viewmodel.ViewportRanges; +import jalview.viewmodel.seqfeatures.FeatureRendererModel; import jalview.viewmodel.seqfeatures.FeatureRendererSettings; import jalview.viewmodel.seqfeatures.FeaturesDisplayed; import jalview.ws.jws2.Jws2Discoverer; @@ -91,6 +158,8 @@ import jalview.xml.binding.jalview.Annotation; import jalview.xml.binding.jalview.Annotation.ThresholdLine; import jalview.xml.binding.jalview.AnnotationColourScheme; import jalview.xml.binding.jalview.AnnotationElement; +import jalview.xml.binding.jalview.DoubleMatrix; +import jalview.xml.binding.jalview.DoubleVector; import jalview.xml.binding.jalview.Feature; import jalview.xml.binding.jalview.Feature.OtherData; import jalview.xml.binding.jalview.FeatureMatcherSet.CompoundMatcher; @@ -105,11 +174,17 @@ import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids; import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids.StructureState; import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer; import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer.SecondaryStructure; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.Axis; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMax; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMin; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SequencePoint; import jalview.xml.binding.jalview.JalviewModel.Tree; import jalview.xml.binding.jalview.JalviewModel.UserColours; import jalview.xml.binding.jalview.JalviewModel.Viewport; import jalview.xml.binding.jalview.JalviewModel.Viewport.CalcIdParam; import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns; +import jalview.xml.binding.jalview.JalviewModel.Viewport.Overview; import jalview.xml.binding.jalview.JalviewUserColours; import jalview.xml.binding.jalview.JalviewUserColours.Colour; import jalview.xml.binding.jalview.MapListType.MapListFrom; @@ -117,6 +192,7 @@ import jalview.xml.binding.jalview.MapListType.MapListTo; import jalview.xml.binding.jalview.Mapping; import jalview.xml.binding.jalview.NoValueColour; import jalview.xml.binding.jalview.ObjectFactory; +import jalview.xml.binding.jalview.PcaDataType; import jalview.xml.binding.jalview.Pdbentry.Property; import jalview.xml.binding.jalview.Sequence; import jalview.xml.binding.jalview.Sequence.DBRef; @@ -125,54 +201,6 @@ import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties; import jalview.xml.binding.jalview.ThresholdType; import jalview.xml.binding.jalview.VAMSAS; -import java.awt.Color; -import java.awt.Font; -import java.awt.Rectangle; -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.math.BigInteger; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Vector; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; -import java.util.jar.JarOutputStream; - -import javax.swing.JInternalFrame; -import javax.swing.SwingUtilities; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Marshaller; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamReader; - /** * Write out the current jalview desktop state as a Jalview XML stream. * @@ -185,12 +213,27 @@ import javax.xml.stream.XMLStreamReader; */ public class Jalview2XML { + + // BH 2018 we add the .jvp binary extension to J2S so that + // it will declare that binary when we do the file save from the browser + + static + { + Platform.addJ2SBinaryType(".jvp?"); + } + private static final String VIEWER_PREFIX = "viewer_"; private static final String RNA_PREFIX = "rna_"; private static final String UTF_8 = "UTF-8"; + /** + * prefix for recovering datasets for alignments with multiple views where + * non-existent dataset IDs were written for some views + */ + private static final String UNIQSEQSETID = "uniqueSeqSetId."; + // use this with nextCounter() to make unique names for entities private int counter = 0; @@ -226,6 +269,45 @@ public class Jalview2XML private Map rnaSessions = new HashMap<>(); /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the boolean value, or false + * if null. + * + * @param b + * @return + */ + public static boolean safeBoolean(Boolean b) + { + return b == null ? false : b.booleanValue(); + } + + /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the integer value, or zero + * if null. + * + * @param i + * @return + */ + public static int safeInt(Integer i) + { + return i == null ? 0 : i.intValue(); + } + + /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the float value, or zero if + * null. + * + * @param f + * @return + */ + public static float safeFloat(Float f) + { + return f == null ? 0f : f.floatValue(); + } + + /** * create/return unique hash string for sq * * @param sq @@ -383,7 +465,7 @@ public class Jalview2XML public boolean isResolvable() { return super.isResolvable() && mp.getTo() != null; - }; + } @Override boolean resolve() @@ -483,24 +565,30 @@ public class Jalview2XML public void saveState(File statefile) { FileOutputStream fos = null; + try { + fos = new FileOutputStream(statefile); + JarOutputStream jout = new JarOutputStream(fos); saveState(jout); + fos.close(); } catch (Exception e) { + Cache.log.error("Couln't write Jalview state to " + statefile, e); // TODO: inform user of the problem - they need to know if their data was // not saved ! if (errorMessage == null) { - errorMessage = "Couldn't write Jalview Archive to output file '" + errorMessage = "Did't write Jalview Archive to output file '" + statefile + "' - See console error log for details"; } else { - errorMessage += "(output file was '" + statefile + "')"; + errorMessage += "(Didn't write Jalview Archive to output file '" + + statefile + ")"; } e.printStackTrace(); } finally @@ -609,7 +697,6 @@ public class Jalview2XML } catch (Exception foo) { } - ; jout.close(); } catch (Exception ex) { @@ -670,7 +757,12 @@ public class Jalview2XML { try { - FileOutputStream fos = new FileOutputStream(jarFile); + // create backupfiles object and get new temp filename destination + boolean doBackup = BackupFiles.getEnabled(); + BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null; + FileOutputStream fos = new FileOutputStream(doBackup ? + backupfiles.getTempFilePath() : jarFile); + JarOutputStream jout = new JarOutputStream(fos); List frames = new ArrayList<>(); @@ -690,9 +782,16 @@ public class Jalview2XML } catch (Exception foo) { } - ; jout.close(); - return true; + boolean success = true; + + if (doBackup) + { + backupfiles.setWriteSuccess(success); + success = backupfiles.rollBackupsAndRenameTempFile(); + } + + return success; } catch (Exception ex) { errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details"; @@ -992,25 +1091,28 @@ public class Jalview2XML if (frames[f] instanceof StructureViewerBase) { StructureViewerBase viewFrame = (StructureViewerBase) frames[f]; - matchedFile = saveStructureState(ap, jds, pdb, entry, viewIds, + matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds, matchedFile, viewFrame); /* * Only store each structure viewer's state once in the project * jar. First time through only (storeDS==false) */ String viewId = viewFrame.getViewId(); + String viewerType = viewFrame.getViewerType().toString(); if (!storeDS && !viewIds.contains(viewId)) { viewIds.add(viewId); - try + File viewerState = viewFrame.saveSession(); + if (viewerState != null) { - String viewerState = viewFrame.getStateInfo(); - writeJarEntry(jout, getViewerJarEntryName(viewId), - viewerState.getBytes()); - } catch (IOException e) + copyFileToJar(jout, viewerState.getPath(), + getViewerJarEntryName(viewId), viewerType); + } + else { - System.err.println( - "Error saving viewer state: " + e.getMessage()); + Cache.log.error("Failed to save viewer state for " + + + viewerType); } } } @@ -1032,7 +1134,7 @@ public class Jalview2XML if (!pdbfiles.contains(pdbId)) { pdbfiles.add(pdbId); - copyFileToJar(jout, matchedFile, pdbId); + copyFileToJar(jout, matchedFile, pdbId, pdbId); } } @@ -1169,6 +1271,9 @@ public class Jalview2XML tree.setXpos(tp.getX()); tree.setYpos(tp.getY()); tree.setId(makeHashCode(tp, null)); + tree.setLinkToAllViews( + tp.getTreeCanvas().isApplyToAllViews()); + // jms.addTree(tree); object.getTree().add(tree); } @@ -1177,6 +1282,24 @@ public class Jalview2XML } } + /* + * save PCA viewers + */ + if (!storeDS && Desktop.desktop != null) + { + for (JInternalFrame frame : Desktop.desktop.getAllFrames()) + { + if (frame instanceof PCAPanel) + { + PCAPanel panel = (PCAPanel) frame; + if (panel.getAlignViewport().getAlignment() == jal) + { + savePCA(panel, object); + } + } + } + } + // SAVE ANNOTATIONS /** * store forward refs from an annotationRow to any groups @@ -1327,6 +1450,24 @@ public class Jalview2XML view.setStartRes(vpRanges.getStartRes()); view.setStartSeq(vpRanges.getStartSeq()); + OverviewPanel ov = ap.getOverviewPanel(); + if (ov != null) + { + Overview overview = new Overview(); + Rectangle bounds = ov.getBounds(); + overview.setXpos(bounds.x); + overview.setYpos(bounds.y); + overview.setWidth(bounds.width); + overview.setHeight(bounds.height); + overview.setShowHidden(ov.isShowHiddenRegions()); + overview.setGapColour(ov.getCanvas().getGapColour().getRGB()); + overview.setResidueColour(ov.getCanvas().getResidueColour().getRGB()); + overview.setHiddenColour(ov.getCanvas().getHiddenColour().getRGB()); + String title = ((JInternalFrame) SwingUtilities + .getAncestorOfClass(JInternalFrame.class, ov)).getTitle(); + overview.setTitle(title); + view.setOverview(overview); + } if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme) { view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(), @@ -1395,11 +1536,14 @@ public class Jalview2XML view.setFollowHighlight(av.isFollowHighlight()); view.setFollowSelection(av.followSelection); view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus()); + view.setShowComplementFeatures(av.isShowComplementFeatures()); + view.setShowComplementFeaturesOnTop( + av.isShowComplementFeaturesOnTop()); if (av.getFeaturesDisplayed() != null) { FeatureSettings fs = new FeatureSettings(); - FeatureRenderer fr = ap.getSeqPanel().seqCanvas + FeatureRendererModel fr = ap.getSeqPanel().seqCanvas .getFeatureRenderer(); String[] renderOrder = fr.getRenderOrder().toArray(new String[0]); @@ -1555,6 +1699,7 @@ public class Jalview2XML // using save and then load try { + fileName = fileName.replace('\\', '/'); System.out.println("Writing jar entry " + fileName); JarEntry entry = new JarEntry(fileName); jout.putNextEntry(entry); @@ -1584,6 +1729,196 @@ public class Jalview2XML } /** + * Writes PCA viewer attributes and computed values to an XML model object and + * adds it to the JalviewModel. Any exceptions are reported by logging. + */ + protected void savePCA(PCAPanel panel, JalviewModel object) + { + try + { + PcaViewer viewer = new PcaViewer(); + viewer.setHeight(panel.getHeight()); + viewer.setWidth(panel.getWidth()); + viewer.setXpos(panel.getX()); + viewer.setYpos(panel.getY()); + viewer.setTitle(panel.getTitle()); + PCAModel pcaModel = panel.getPcaModel(); + viewer.setScoreModelName(pcaModel.getScoreModelName()); + viewer.setXDim(panel.getSelectedDimensionIndex(X)); + viewer.setYDim(panel.getSelectedDimensionIndex(Y)); + viewer.setZDim(panel.getSelectedDimensionIndex(Z)); + viewer.setBgColour( + panel.getRotatableCanvas().getBackgroundColour().getRGB()); + viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor()); + float[] spMin = panel.getRotatableCanvas().getSeqMin(); + SeqPointMin spmin = new SeqPointMin(); + spmin.setXPos(spMin[0]); + spmin.setYPos(spMin[1]); + spmin.setZPos(spMin[2]); + viewer.setSeqPointMin(spmin); + float[] spMax = panel.getRotatableCanvas().getSeqMax(); + SeqPointMax spmax = new SeqPointMax(); + spmax.setXPos(spMax[0]); + spmax.setYPos(spMax[1]); + spmax.setZPos(spMax[2]); + viewer.setSeqPointMax(spmax); + viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels()); + viewer.setLinkToAllViews( + panel.getRotatableCanvas().isApplyToAllViews()); + SimilarityParamsI sp = pcaModel.getSimilarityParameters(); + viewer.setIncludeGaps(sp.includeGaps()); + viewer.setMatchGaps(sp.matchGaps()); + viewer.setIncludeGappedColumns(sp.includeGappedColumns()); + viewer.setDenominateByShortestLength(sp.denominateByShortestLength()); + + /* + * sequence points on display + */ + for (jalview.datamodel.SequencePoint spt : pcaModel + .getSequencePoints()) + { + SequencePoint point = new SequencePoint(); + point.setSequenceRef(seqHash(spt.getSequence())); + point.setXPos(spt.coord.x); + point.setYPos(spt.coord.y); + point.setZPos(spt.coord.z); + viewer.getSequencePoint().add(point); + } + + /* + * (end points of) axes on display + */ + for (Point p : panel.getRotatableCanvas().getAxisEndPoints()) + { + + Axis axis = new Axis(); + axis.setXPos(p.x); + axis.setYPos(p.y); + axis.setZPos(p.z); + viewer.getAxis().add(axis); + } + + /* + * raw PCA data (note we are not restoring PCA inputs here - + * alignment view, score model, similarity parameters) + */ + PcaDataType data = new PcaDataType(); + viewer.setPcaData(data); + PCA pca = pcaModel.getPcaData(); + + DoubleMatrix pm = new DoubleMatrix(); + saveDoubleMatrix(pca.getPairwiseScores(), pm); + data.setPairwiseMatrix(pm); + + DoubleMatrix tm = new DoubleMatrix(); + saveDoubleMatrix(pca.getTridiagonal(), tm); + data.setTridiagonalMatrix(tm); + + DoubleMatrix eigenMatrix = new DoubleMatrix(); + data.setEigenMatrix(eigenMatrix); + saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix); + + object.getPcaViewer().add(viewer); + } catch (Throwable t) + { + Cache.log.error("Error saving PCA: " + t.getMessage()); + } + } + + /** + * Stores values from a matrix into an XML element, including (if present) the + * D or E vectors + * + * @param m + * @param xmlMatrix + * @see #loadDoubleMatrix(DoubleMatrix) + */ + protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix) + { + xmlMatrix.setRows(m.height()); + xmlMatrix.setColumns(m.width()); + for (int i = 0; i < m.height(); i++) + { + DoubleVector row = new DoubleVector(); + for (int j = 0; j < m.width(); j++) + { + row.getV().add(m.getValue(i, j)); + } + xmlMatrix.getRow().add(row); + } + if (m.getD() != null) + { + DoubleVector dVector = new DoubleVector(); + for (double d : m.getD()) + { + dVector.getV().add(d); + } + xmlMatrix.setD(dVector); + } + if (m.getE() != null) + { + DoubleVector eVector = new DoubleVector(); + for (double e : m.getE()) + { + eVector.getV().add(e); + } + xmlMatrix.setE(eVector); + } + } + + /** + * Loads XML matrix data into a new Matrix object, including the D and/or E + * vectors (if present) + * + * @param mData + * @return + * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix) + */ + protected MatrixI loadDoubleMatrix(DoubleMatrix mData) + { + int rows = mData.getRows(); + double[][] vals = new double[rows][]; + + for (int i = 0; i < rows; i++) + { + List dVector = mData.getRow().get(i).getV(); + vals[i] = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vals[i][dvi++] = d; + } + } + + MatrixI m = new Matrix(vals); + + if (mData.getD() != null) + { + List dVector = mData.getD().getV(); + double[] vec = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vec[dvi++] = d; + } + m.setD(vec); + } + if (mData.getE() != null) + { + List dVector = mData.getE().getV(); + double[] vec = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vec[dvi++] = d; + } + m.setE(vec); + } + + return m; + } + + /** * Save any Varna viewers linked to this sequence. Writes an rnaViewer element * for each viewer, with *
    @@ -1665,7 +2000,7 @@ public class Jalview2XML String varnaStateFile = varna.getStateInfo(model.rna); jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter(); - copyFileToJar(jout, varnaStateFile, jarEntryName); + copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna"); rnaSessions.put(model, jarEntryName); } SecondaryStructure ss = new SecondaryStructure(); @@ -1689,58 +2024,48 @@ public class Jalview2XML * @param jout * @param infilePath * @param jarEntryName + * @param msg + * additional identifying info to log to the console */ protected void copyFileToJar(JarOutputStream jout, String infilePath, - String jarEntryName) + String jarEntryName, String msg) { - DataInputStream dis = null; - try + try (InputStream is = new FileInputStream(infilePath)) { File file = new File(infilePath); if (file.exists() && jout != null) { - dis = new DataInputStream(new FileInputStream(file)); - byte[] data = new byte[(int) file.length()]; - dis.readFully(data); - writeJarEntry(jout, jarEntryName, data); + System.out.println( + "Writing jar entry " + jarEntryName + " (" + msg + ")"); + jout.putNextEntry(new JarEntry(jarEntryName)); + copyAll(is, jout); + jout.closeEntry(); + // dis = new DataInputStream(new FileInputStream(file)); + // byte[] data = new byte[(int) file.length()]; + // dis.readFully(data); + // writeJarEntry(jout, jarEntryName, data); } } catch (Exception ex) { ex.printStackTrace(); - } finally - { - if (dis != null) - { - try - { - dis.close(); - } catch (IOException e) - { - // ignore - } - } } } /** - * Write the data to a new entry of given name in the output jar file + * Copies input to output, in 4K buffers; handles any data (text or binary) * - * @param jout - * @param jarEntryName - * @param data + * @param in + * @param out * @throws IOException */ - protected void writeJarEntry(JarOutputStream jout, String jarEntryName, - byte[] data) throws IOException + protected void copyAll(InputStream in, OutputStream out) + throws IOException { - if (jout != null) + byte[] buffer = new byte[4096]; + int bytesRead = 0; + while ((bytesRead = in.read(buffer)) != -1) { - System.out.println("Writing jar entry " + jarEntryName); - jout.putNextEntry(new JarEntry(jarEntryName)); - DataOutputStream dout = new DataOutputStream(jout); - dout.write(data, 0, data.length); - dout.flush(); - jout.closeEntry(); + out.write(buffer, 0, bytesRead); } } @@ -1757,7 +2082,7 @@ public class Jalview2XML * @param viewFrame * @return */ - protected String saveStructureState(AlignmentPanel ap, SequenceI jds, + protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds, Pdbids pdb, PDBEntry entry, List viewIds, String matchedFile, StructureViewerBase viewFrame) { @@ -1811,7 +2136,7 @@ public class Jalview2XML final String viewId = viewFrame.getViewId(); state.setViewId(viewId); state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap)); - state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap)); + state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap)); state.setColourByJmol(viewFrame.isColouredByViewer()); state.setType(viewFrame.getViewerType().toString()); // pdb.addStructureState(state); @@ -2195,7 +2520,7 @@ public class Jalview2XML vamsasSeq.setName(jds.getName()); vamsasSeq.setSequence(jds.getSequenceAsString()); vamsasSeq.setDescription(jds.getDescription()); - jalview.datamodel.DBRefEntry[] dbrefs = null; + List dbrefs = null; if (jds.getDatasetSequence() != null) { vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence())); @@ -2211,21 +2536,29 @@ public class Jalview2XML parentseq = jds; } } + + /* + * save any dbrefs; special subclass GeneLocus is flagged as 'locus' + */ if (dbrefs != null) { - for (int d = 0; d < dbrefs.length; d++) + for (int d = 0, nd = dbrefs.size(); d < nd; d++) { DBRef dbref = new DBRef(); - dbref.setSource(dbrefs[d].getSource()); - dbref.setVersion(dbrefs[d].getVersion()); - dbref.setAccessionId(dbrefs[d].getAccessionId()); - if (dbrefs[d].hasMap()) + DBRefEntry ref = dbrefs.get(d); + dbref.setSource(ref.getSource()); + dbref.setVersion(ref.getVersion()); + dbref.setAccessionId(ref.getAccessionId()); + if (ref instanceof GeneLocus) + { + dbref.setLocus(true); + } + if (ref.hasMap()) { - Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq, + Mapping mp = createVamsasMapping(ref.getMap(), parentseq, jds, recurse); dbref.setMapping(mp); } - // vamsasSeq.addDBRef(dbref); vamsasSeq.getDBRef().add(dbref); } } @@ -2417,7 +2750,7 @@ public class Jalview2XML * @param file * - HTTP URL or filename */ - public AlignFrame loadJalviewAlign(final String file) + public AlignFrame loadJalviewAlign(final Object file) { jalview.gui.AlignFrame af = null; @@ -2451,7 +2784,7 @@ public class Jalview2XML public void run() { setLoadingFinishedForNewStructureViewers(); - }; + } }); } catch (Exception x) { @@ -2461,62 +2794,70 @@ public class Jalview2XML return af; } - private jarInputStreamProvider createjarInputStreamProvider( - final String file) throws MalformedURLException + @SuppressWarnings("unused") + private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException { + + // BH 2018 allow for bytes already attached to File object + try { + String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString()); + byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile) + : null; + URL url = null; + errorMessage = null; + uniqueSetSuffix = null; + seqRefIds = null; + viewportsAdded.clear(); + frefedSequence = null; + + if (file.startsWith("http://")) { + url = new URL(file); + } + final URL _url = url; + return new jarInputStreamProvider() { + + @Override + public JarInputStream getJarInputStream() throws IOException { + if (bytes != null) { +// System.out.println("Jalview2XML: opening byte jarInputStream for bytes.length=" + bytes.length); + return new JarInputStream(new ByteArrayInputStream(bytes)); + } + if (_url != null) { +// System.out.println("Jalview2XML: opening url jarInputStream for " + _url); + return new JarInputStream(_url.openStream()); + } else { +// System.out.println("Jalview2XML: opening file jarInputStream for " + file); + return new JarInputStream(new FileInputStream(file)); + } + } + + @Override + public String getFilename() { + return file; + } + }; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Recover jalview session from a jalview project archive. Caller may + * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence + * themselves. Any null fields will be initialised with default values, + * non-null fields are left alone. + * + * @param jprovider + * @return + */ + public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider) { - URL url = null; errorMessage = null; - uniqueSetSuffix = null; - seqRefIds = null; - viewportsAdded.clear(); - frefedSequence = null; - - if (file.startsWith("http://")) + if (uniqueSetSuffix == null) { - url = new URL(file); + uniqueSetSuffix = System.currentTimeMillis() % 100000 + ""; } - final URL _url = url; - return new jarInputStreamProvider() - { - - @Override - public JarInputStream getJarInputStream() throws IOException - { - if (_url != null) - { - return new JarInputStream(_url.openStream()); - } - else - { - return new JarInputStream(new FileInputStream(file)); - } - } - - @Override - public String getFilename() - { - return file; - } - }; - } - - /** - * Recover jalview session from a jalview project archive. Caller may - * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence - * themselves. Any null fields will be initialised with default values, - * non-null fields are left alone. - * - * @param jprovider - * @return - */ - public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider) - { - errorMessage = null; - if (uniqueSetSuffix == null) - { - uniqueSetSuffix = System.currentTimeMillis() % 100000 + ""; - } - if (seqRefIds == null) + if (seqRefIds == null) { initSeqRefs(); } @@ -2540,9 +2881,6 @@ public class Jalview2XML if (jarentry != null && jarentry.getName().endsWith(".xml")) { - InputStreamReader in = new InputStreamReader(jin, UTF_8); - // JalviewModel object = new JalviewModel(); - JAXBContext jc = JAXBContext .newInstance("jalview.xml.binding.jalview"); XMLStreamReader streamReader = XMLInputFactory.newInstance() @@ -2552,11 +2890,6 @@ public class Jalview2XML .unmarshal(streamReader, JalviewModel.class); JalviewModel object = jbe.getValue(); - /* - Unmarshaller unmar = new Unmarshaller(object); - unmar.setValidation(false); - object = (JalviewModel) unmar.unmarshal(in); - */ if (true) // !skipViewport(object)) { _af = loadFromObject(object, file, true, jprovider); @@ -2603,16 +2936,7 @@ public class Jalview2XML ex.printStackTrace(System.err); if (attemptversion1parse) { - // Is Version 1 Jar file? - try - { - af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider); - } catch (Exception ex2) - { - System.err.println("Exception whilst loading as jalviewXMLV1:"); - ex2.printStackTrace(); - af = null; - } + // used to attempt to parse as V1 castor-generated xml } if (Desktop.instance != null) { @@ -2732,8 +3056,8 @@ public class Jalview2XML if (!addedToSplitFrames.contains(af)) { Viewport view = candidate.getKey(); - Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(), - view.getHeight()); + Desktop.addInternalFrame(af, view.getTitle(), + safeInt(view.getWidth()), safeInt(view.getHeight())); af.setMenusForViewport(); System.err.println("Failed to restore view " + view.getTitle() + " to split frame"); @@ -2865,53 +3189,42 @@ public class Jalview2XML * @param prefix * a prefix for the temporary file name, must be at least three * characters long - * @param origFile + * @param suffixModel * null or original file - so new file can be given the same suffix * as the old one * @return */ protected String copyJarEntry(jarInputStreamProvider jprovider, - String jarEntryName, String prefix, String origFile) + String jarEntryName, String prefix, String suffixModel) { - BufferedReader in = null; - PrintWriter out = null; String suffix = ".tmp"; - if (origFile == null) + if (suffixModel == null) { - origFile = jarEntryName; + suffixModel = jarEntryName; } - int sfpos = origFile.lastIndexOf("."); - if (sfpos > -1 && sfpos < (origFile.length() - 3)) + int sfpos = suffixModel.lastIndexOf("."); + if (sfpos > -1 && sfpos < (suffixModel.length() - 1)) { - suffix = "." + origFile.substring(sfpos + 1); + suffix = "." + suffixModel.substring(sfpos + 1); } - try - { - JarInputStream jin = jprovider.getJarInputStream(); - /* - * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new - * URL(jprovider).openStream()); } else { jin = new JarInputStream(new - * FileInputStream(jprovider)); } - */ + try (JarInputStream jin = jprovider.getJarInputStream()) + { JarEntry entry = null; do { entry = jin.getNextJarEntry(); } while (entry != null && !entry.getName().equals(jarEntryName)); + if (entry != null) { - in = new BufferedReader(new InputStreamReader(jin, UTF_8)); + // in = new BufferedReader(new InputStreamReader(jin, UTF_8)); File outFile = File.createTempFile(prefix, suffix); outFile.deleteOnExit(); - out = new PrintWriter(new FileOutputStream(outFile)); - String data; - - while ((data = in.readLine()) != null) + try (OutputStream os = new FileOutputStream(outFile)) { - out.println(data); + copyAll(jin, os); } - out.flush(); String t = outFile.getAbsolutePath(); return t; } @@ -2922,22 +3235,6 @@ public class Jalview2XML } catch (Exception ex) { ex.printStackTrace(); - } finally - { - if (in != null) - { - try - { - in.close(); - } catch (IOException e) - { - // ignore - } - } - if (out != null) - { - out.close(); - } } return null; @@ -2990,6 +3287,28 @@ public class Jalview2XML : null; // //////////////////////////////// + // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID + // + // + // If we just load in the same jar file again, the sequenceSetId + // will be the same, and we end up with multiple references + // to the same sequenceSet. We must modify this id on load + // so that each load of the file gives a unique id + + /** + * used to resolve correct alignment dataset for alignments with multiple + * views + */ + String uniqueSeqSetId = null; + String viewId = null; + if (view != null) + { + uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix; + viewId = (view.getId() == null ? null + : view.getId() + uniqueSetSuffix); + } + + // //////////////////////////////// // LOAD SEQUENCES List hiddenSeqs = null; @@ -3016,8 +3335,10 @@ public class Jalview2XML || tmpSeq.getEnd() != jseq.getEnd()) { System.err.println( - "Warning JAL-2154 regression: updating start/end for sequence " - + tmpSeq.toString() + " to " + jseq); + String.format("Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d", + tmpSeq.getName(), tmpSeq.getStart(), + tmpSeq.getEnd(), jseq.getStart(), + jseq.getEnd())); } } else @@ -3057,8 +3378,7 @@ public class Jalview2XML vi++; } - if (jseq.isViewreference() != null - && jseq.isViewreference().booleanValue()) + if (safeBoolean(jseq.isViewreference())) { referenceseqForView = tmpseqs.get(tmpseqs.size() - 1); } @@ -3111,7 +3431,7 @@ public class Jalview2XML // finally, verify all data in vamsasSet is actually present in al // passing on flag indicating if it is actually a stored dataset - recoverDatasetFor(vamsasSet, al, isdsal); + recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId); } if (referenceseqForView != null) @@ -3150,7 +3470,7 @@ public class Jalview2XML Feature feat = features.get(f); SequenceFeature sf = new SequenceFeature(feat.getType(), feat.getDescription(), feat.getBegin(), feat.getEnd(), - feat.getScore(), feat.getFeatureGroup()); + safeFloat(feat.getScore()), feat.getFeatureGroup()); sf.setStatus(feat.getStatus()); /* @@ -3344,22 +3664,21 @@ public class Jalview2XML // annotation.setAutoCalculated(true); // } } - if (autoForView || // (annotation.hasAutoCalculated() && - annotation.isAutoCalculated()) + if (autoForView || annotation.isAutoCalculated()) { // remove ID - we don't recover annotation from other views for // view-specific annotation annotation.setId(null); } - // set visiblity for other annotation in this view + // set visibility for other annotation in this view String annotationId = annotation.getId(); if (annotationId != null && annotationIds.containsKey(annotationId)) { AlignmentAnnotation jda = annotationIds.get(annotationId); // in principle Visible should always be true for annotation displayed // in multiple views - if (annotation.isVisible() != null) // annotation.hasVisible()) + if (annotation.isVisible() != null) { jda.visible = annotation.isVisible(); } @@ -3386,8 +3705,7 @@ public class Jalview2XML continue; } - float value = annElement.getValue() == null ? 0f - : annElement.getValue().floatValue(); + float value = safeFloat(annElement.getValue()); anot[anpos] = new jalview.datamodel.Annotation( annElement.getDisplayCharacter(), annElement.getDescription(), @@ -3399,16 +3717,7 @@ public class Jalview2XML .getSecondaryStructure() .charAt(0), value); - // JBPNote: Consider verifying dataflow for IO of secondary - // structure annotation read from Stockholm files - // this was added to try to ensure that - // if (anot[ annElement.getPosition()].secondaryStructure>' ') - // { - // anot[ annElement.getPosition()].displayCharacter = ""; - // } - final int colourValue = annElement.getColour() == null ? 0 - : annElement.getColour().intValue(); - anot[anpos].colour = new java.awt.Color(colourValue); + anot[anpos].colour = new Color(safeInt(annElement.getColour())); if (firstColour == null) { firstColour = anot[anpos].colour; @@ -3425,18 +3734,17 @@ public class Jalview2XML // } jaa = new jalview.datamodel.AlignmentAnnotation( annotation.getLabel(), annotation.getDescription(), anot, - llim, hlim, annotation.getGraphType()); + llim, hlim, safeInt(annotation.getGraphType())); - jaa.graphGroup = annotation.getGraphGroup(); + jaa.graphGroup = safeInt(annotation.getGraphGroup()); jaa._linecolour = firstColour; if (annotation.getThresholdLine() != null) { jaa.setThreshold(new jalview.datamodel.GraphLine( - annotation.getThresholdLine().getValue(), + safeFloat(annotation.getThresholdLine().getValue()), annotation.getThresholdLine().getLabel(), - new java.awt.Color( - annotation.getThresholdLine().getColour()))); - + new java.awt.Color(safeInt( + annotation.getThresholdLine().getColour())))); } if (autoForView || annotation.isAutoCalculated()) { @@ -3490,7 +3798,7 @@ public class Jalview2XML if (annotation.getScore() != null) { - jaa.setScore(annotation.getScore()); + jaa.setScore(annotation.getScore().doubleValue()); } if (annotation.isVisible() != null) { @@ -3507,8 +3815,7 @@ public class Jalview2XML { jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue(); } - if (/*annotation.hasAutoCalculated() && */annotation - .isAutoCalculated()) + if (annotation.isAutoCalculated()) { // newer files have an 'autoCalculated' flag and store calculation // state in viewport properties @@ -3519,11 +3826,7 @@ public class Jalview2XML { jaa.graphHeight = annotation.getGraphHeight().intValue(); } - // if (annotation.hasBelowAlignment()) - // { - // schema specifies default for optional attribute - jaa.belowAlignment = annotation.isBelowAlignment(); - // } + jaa.belowAlignment = annotation.isBelowAlignment(); jaa.setCalcId(annotation.getCalcId()); if (annotation.getProperty().size() > 0) { @@ -3570,12 +3873,11 @@ public class Jalview2XML } else { - cs = ColourSchemeProperty.getColourScheme(al, + cs = ColourSchemeProperty.getColourScheme(null, al, jGroup.getColour()); } } - int pidThreshold = jGroup.getPidThreshold() == null ? 0 - : jGroup.getPidThreshold().intValue(); + int pidThreshold = safeInt(jGroup.getPidThreshold()); Vector seqs = new Vector<>(); @@ -3596,40 +3898,24 @@ public class Jalview2XML } SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs, - jGroup.isDisplayBoxes(), jGroup.isDisplayText(), - jGroup.isColourText(), jGroup.getStart(), jGroup.getEnd()); + safeBoolean(jGroup.isDisplayBoxes()), + safeBoolean(jGroup.isDisplayText()), + safeBoolean(jGroup.isColourText()), + safeInt(jGroup.getStart()), safeInt(jGroup.getEnd())); sg.getGroupColourScheme().setThreshold(pidThreshold, true); sg.getGroupColourScheme() - .setConservationInc(jGroup.getConsThreshold() == null ? 0 - : jGroup.getConsThreshold().intValue()); - sg.setOutlineColour( - new java.awt.Color(jGroup.getOutlineColour() == null ? 0 - : jGroup.getOutlineColour().intValue())); - - sg.textColour = new java.awt.Color(jGroup.getTextCol1()); - sg.textColour2 = new java.awt.Color(jGroup.getTextCol2()); - sg.setShowNonconserved( - jGroup.isShowUnconserved() != null - ? jGroup.isShowUnconserved().booleanValue() - : false); - sg.thresholdTextColour = jGroup.getTextColThreshold(); + .setConservationInc(safeInt(jGroup.getConsThreshold())); + sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour()))); + + sg.textColour = new Color(safeInt(jGroup.getTextCol1())); + sg.textColour2 = new Color(safeInt(jGroup.getTextCol2())); + sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved())); + sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold()); // attributes with a default in the schema are never null - // if (jGroup.hasShowConsensusHistogram()) - // { sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram()); - // } - // if (jGroup.hasShowSequenceLogo()) - // { sg.setshowSequenceLogo(jGroup.isShowSequenceLogo()); - // } - // if (jGroup.hasNormaliseSequenceLogo()) - // { sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo()); - // } - // if (jGroup.hasIgnoreGapsinConsensus()) - // { sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus()); - // } if (jGroup.getConsThreshold() != null && jGroup.getConsThreshold().intValue() != 0) { @@ -3685,13 +3971,6 @@ public class Jalview2XML // /////////////////////////////// // LOAD VIEWPORT - // If we just load in the same jar file again, the sequenceSetId - // will be the same, and we end up with multiple references - // to the same sequenceSet. We must modify this id on load - // so that each load of the file gives a unique id - String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix; - String viewId = (view.getId() == null ? null - : view.getId() + uniqueSetSuffix); AlignFrame af = null; AlignViewport av = null; // now check to see if we really need to create a new viewport. @@ -3766,21 +4045,51 @@ public class Jalview2XML } /* - * Load any trees, PDB structures and viewers + * Load any trees, PDB structures and viewers, Overview * * Not done if flag is false (when this method is used for New View) */ if (loadTreesAndStructures) { loadTrees(jalviewModel, view, af, av, ap); + loadPCAViewers(jalviewModel, ap); loadPDBStructures(jprovider, jseqs, af, ap); loadRnaViewers(jprovider, jseqs, ap); + loadOverview(view, af); } // and finally return. return af; } /** + * Load Overview window, restoring colours, 'show hidden regions' flag, title + * and geometry as saved + * + * @param view + * @param af + */ + protected void loadOverview(Viewport view, AlignFrame af) + { + Overview overview = view.getOverview(); + if (overview != null) + { + OverviewPanel overviewPanel = af + .openOverviewPanel(overview.isShowHidden()); + overviewPanel.setBounds(overview.getXpos(), overview.getYpos(), + overview.getWidth(), overview.getHeight()); + overviewPanel.setPreferredSize( + new Dimension(overview.getWidth(), overview.getHeight())); + Color gap = new Color(overview.getGapColour()); + Color residue = new Color(overview.getResidueColour()); + Color hidden = new Color(overview.getHiddenColour()); + overviewPanel.getCanvas().setColours(gap, residue, hidden); + ((JInternalFrame) SwingUtilities + .getAncestorOfClass(JInternalFrame.class, overviewPanel)) + .setTitle(overview.getTitle()); + } + } + + /** * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna * panel is restored from separate jar entries, two (gapped and trimmed) per * sequence and secondary structure. @@ -3819,7 +4128,7 @@ public class Jalview2XML * add the structure to the Varna display (with session state copied * from the jar to a temporary file) */ - boolean gapped = ss.isGapped(); + boolean gapped = safeBoolean(ss.isGapped()); String rnaTitle = ss.getTitle(); String sessionState = ss.getViewerState(); String tempStateFile = copyJarEntry(jprovider, sessionState, @@ -3827,7 +4136,7 @@ public class Jalview2XML RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped); appVarna.addModelSession(rna, rnaTitle, tempStateFile); } - appVarna.setInitialSelection(viewer.getSelectedRna()); + appVarna.setInitialSelection(safeInt(viewer.getSelectedRna())); } } } @@ -3868,8 +4177,9 @@ public class Jalview2XML * viewer not found - make it */ RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(), - viewer.getXpos(), viewer.getYpos(), viewer.getWidth(), - viewer.getHeight(), viewer.getDividerLocation()); + safeInt(viewer.getXpos()), safeInt(viewer.getYpos()), + safeInt(viewer.getWidth()), safeInt(viewer.getHeight()), + safeInt(viewer.getDividerLocation())); AppVarna varna = new AppVarna(model, ap); return varna; @@ -3898,10 +4208,10 @@ public class Jalview2XML TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId()); if (tp == null) { - tp = af.showNewickTree( - new jalview.io.NewickFile(tree.getNewick()), - tree.getTitle(), tree.getWidth(), tree.getHeight(), - tree.getXpos(), tree.getYpos()); + tp = af.showNewickTree(new NewickFile(tree.getNewick()), + tree.getTitle(), safeInt(tree.getWidth()), + safeInt(tree.getHeight()), safeInt(tree.getXpos()), + safeInt(tree.getYpos())); if (tree.getId() != null) { // perhaps bind the tree id to something ? @@ -3913,15 +4223,15 @@ public class Jalview2XML // TODO: should check if tp has been manipulated by user - if so its // settings shouldn't be modified tp.setTitle(tree.getTitle()); - tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), - tree.getWidth(), tree.getHeight())); - tp.setViewport(av); // af.viewport; // TODO: verify 'associate with all - // views' - // works still + tp.setBounds(new Rectangle(safeInt(tree.getXpos()), + safeInt(tree.getYpos()), safeInt(tree.getWidth()), + safeInt(tree.getHeight()))); + tp.setViewport(av); // af.viewport; + // TODO: verify 'associate with all views' works still tp.getTreeCanvas().setViewport(av); // af.viewport; tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel; - } + tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews()); if (tp == null) { warn("There was a problem recovering stored Newick tree: \n" @@ -3929,27 +4239,29 @@ public class Jalview2XML continue; } - tp.fitToWindow.setState(tree.isFitToWindow()); + tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow())); tp.fitToWindow_actionPerformed(null); if (tree.getFontName() != null) { - tp.setTreeFont(new java.awt.Font(tree.getFontName(), - tree.getFontStyle(), tree.getFontSize())); + tp.setTreeFont( + new Font(tree.getFontName(), safeInt(tree.getFontStyle()), + safeInt(tree.getFontSize()))); } else { - tp.setTreeFont(new java.awt.Font(view.getFontName(), - view.getFontStyle(), tree.getFontSize())); + tp.setTreeFont( + new Font(view.getFontName(), safeInt(view.getFontStyle()), + safeInt(view.getFontSize()))); } - tp.showPlaceholders(tree.isMarkUnlinked()); - tp.showBootstrap(tree.isShowBootstrap()); - tp.showDistances(tree.isShowDistances()); + tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked())); + tp.showBootstrap(safeBoolean(tree.isShowBootstrap())); + tp.showDistances(safeBoolean(tree.isShowDistances())); - tp.getTreeCanvas().setThreshold(tree.getThreshold()); + tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold())); - if (tree.isCurrentTree()) + if (safeBoolean(tree.isCurrentTree())) { af.getViewport().setCurrentTree(tp.getTree()); } @@ -4003,10 +4315,10 @@ public class Jalview2XML loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile())); jpdb.setId(pdbid.getId()); - int x = structureState.getXpos(); - int y = structureState.getYpos(); - int width = structureState.getWidth(); - int height = structureState.getHeight(); + int x = safeInt(structureState.getXpos()); + int y = safeInt(structureState.getYpos()); + int width = safeInt(structureState.getWidth()); + int height = safeInt(structureState.getHeight()); // Probably don't need to do this anymore... // Desktop.desktop.getComponentAt(x, y); @@ -4022,10 +4334,15 @@ public class Jalview2XML } if (!structureViewers.containsKey(sviewid)) { + String viewerType = structureState.getType(); + if (viewerType == null) // pre Jalview 2.9 + { + viewerType = ViewerType.JMOL.toString(); + } structureViewers.put(sviewid, new StructureViewerModel(x, y, width, height, false, false, true, structureState.getViewId(), - structureState.getType())); + viewerType)); // Legacy pre-2.7 conversion JAL-823 : // do not assume any view has to be linked for colour by // sequence @@ -4126,246 +4443,17 @@ public class Jalview2XML return; } - /* - * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry - * "viewer_"+stateData.viewId - */ - if (ViewerType.CHIMERA.toString().equals(stateData.getType())) - { - createChimeraViewer(viewerData, af, jprovider); - } - else - { - /* - * else Jmol (if pre-2.9, stateData contains JMOL state string) - */ - createJmolViewer(viewerData, af, jprovider); - } - } - - /** - * Create a new Chimera viewer. - * - * @param data - * @param af - * @param jprovider - */ - protected void createChimeraViewer( - Entry viewerData, AlignFrame af, - jarInputStreamProvider jprovider) - { - StructureViewerModel data = viewerData.getValue(); - String chimeraSessionFile = data.getStateData(); - - /* - * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file - * - * NB this is the 'saved' viewId as in the project file XML, _not_ the - * 'uniquified' sviewid used to reconstruct the viewer here - */ - String viewerJarEntryName = getViewerJarEntryName(data.getViewId()); - chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName, - "chimera", null); - - Set> fileData = data.getFileData() - .entrySet(); - List pdbs = new ArrayList<>(); - List allseqs = new ArrayList<>(); - for (Entry pdb : fileData) - { - String filePath = pdb.getValue().getFilePath(); - String pdbId = pdb.getValue().getPdbId(); - // pdbs.add(new PDBEntry(filePath, pdbId)); - pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath)); - final List seqList = pdb.getValue().getSeqList(); - SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]); - allseqs.add(seqs); - } - - boolean colourByChimera = data.isColourByViewer(); - boolean colourBySequence = data.isColourWithAlignPanel(); - - // TODO use StructureViewer as a factory here, see JAL-1761 - final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]); - final SequenceI[][] seqsArray = allseqs - .toArray(new SequenceI[allseqs.size()][]); - String newViewId = viewerData.getKey(); - - ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile, - af.alignPanel, pdbArray, seqsArray, colourByChimera, - colourBySequence, newViewId); - cvf.setSize(data.getWidth(), data.getHeight()); - cvf.setLocation(data.getX(), data.getY()); - } - - /** - * Create a new Jmol window. First parse the Jmol state to translate filenames - * loaded into the view, and record the order in which files are shown in the - * Jmol view, so we can add the sequence mappings in same order. - * - * @param viewerData - * @param af - * @param jprovider - */ - protected void createJmolViewer( - final Entry viewerData, - AlignFrame af, jarInputStreamProvider jprovider) - { - final StructureViewerModel svattrib = viewerData.getValue(); - String state = svattrib.getStateData(); - - /* - * Pre-2.9: state element value is the Jmol state string - * - * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_" - * + viewId - */ - if (ViewerType.JMOL.toString().equals(svattrib.getType())) - { - state = readJarEntry(jprovider, - getViewerJarEntryName(svattrib.getViewId())); - } - - List pdbfilenames = new ArrayList<>(); - List seqmaps = new ArrayList<>(); - List pdbids = new ArrayList<>(); - StringBuilder newFileLoc = new StringBuilder(64); - int cp = 0, ncp, ecp; - Map oldFiles = svattrib.getFileData(); - while ((ncp = state.indexOf("load ", cp)) > -1) - { - do - { - // look for next filename in load statement - newFileLoc.append(state.substring(cp, - ncp = (state.indexOf("\"", ncp + 1) + 1))); - String oldfilenam = state.substring(ncp, - ecp = state.indexOf("\"", ncp)); - // recover the new mapping data for this old filename - // have to normalize filename - since Jmol and jalview do - // filename - // translation differently. - StructureData filedat = oldFiles.get(new File(oldfilenam)); - if (filedat == null) - { - String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\"); - filedat = oldFiles.get(new File(reformatedOldFilename)); - } - newFileLoc.append(Platform.escapeString(filedat.getFilePath())); - pdbfilenames.add(filedat.getFilePath()); - pdbids.add(filedat.getPdbId()); - seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0])); - newFileLoc.append("\""); - cp = ecp + 1; // advance beyond last \" and set cursor so we can - // look for next file statement. - } while ((ncp = state.indexOf("/*file*/", cp)) > -1); - } - if (cp > 0) - { - // just append rest of state - newFileLoc.append(state.substring(cp)); - } - else - { - System.err.print("Ignoring incomplete Jmol state for PDB ids: "); - newFileLoc = new StringBuilder(state); - newFileLoc.append("; load append "); - for (File id : oldFiles.keySet()) - { - // add this and any other pdb files that should be present in - // the viewer - StructureData filedat = oldFiles.get(id); - newFileLoc.append(filedat.getFilePath()); - pdbfilenames.add(filedat.getFilePath()); - pdbids.add(filedat.getPdbId()); - seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0])); - newFileLoc.append(" \""); - newFileLoc.append(filedat.getFilePath()); - newFileLoc.append("\""); - - } - newFileLoc.append(";"); - } - - if (newFileLoc.length() == 0) - { - return; - } - int histbug = newFileLoc.indexOf("history = "); - if (histbug > -1) - { - /* - * change "history = [true|false];" to "history = [1|0];" - */ - histbug += 10; - int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug); - String val = (diff == -1) ? null - : newFileLoc.substring(histbug, diff); - if (val != null && val.length() >= 4) - { - if (val.contains("e")) // eh? what can it be? - { - if (val.trim().equals("true")) - { - val = "1"; - } - else - { - val = "0"; - } - newFileLoc.replace(histbug, diff, val); - } - } - } - - final String[] pdbf = pdbfilenames - .toArray(new String[pdbfilenames.size()]); - final String[] id = pdbids.toArray(new String[pdbids.size()]); - final SequenceI[][] sq = seqmaps - .toArray(new SequenceI[seqmaps.size()][]); - final String fileloc = newFileLoc.toString(); - final String sviewid = viewerData.getKey(); - final AlignFrame alf = af; - final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(), - svattrib.getWidth(), svattrib.getHeight()); + String type = stateData.getType(); try { - javax.swing.SwingUtilities.invokeAndWait(new Runnable() - { - @Override - public void run() - { - JalviewStructureDisplayI sview = null; - try - { - sview = new StructureViewer( - alf.alignPanel.getStructureSelectionManager()) - .createView(StructureViewer.ViewerType.JMOL, - pdbf, id, sq, alf.alignPanel, svattrib, - fileloc, rect, sviewid); - addNewStructureViewer(sview); - } catch (OutOfMemoryError ex) - { - new OOMWarning("restoring structure view for PDB id " + id, - (OutOfMemoryError) ex.getCause()); - if (sview != null && sview.isVisible()) - { - sview.closeViewer(false); - sview.setVisible(false); - sview.dispose(); - } - } - } - }); - } catch (InvocationTargetException ex) - { - warn("Unexpected error when opening Jmol view.", ex); - - } catch (InterruptedException e) + ViewerType viewerType = ViewerType.valueOf(type); + createStructureViewer(viewerType, viewerData, af, jprovider); + } catch (IllegalArgumentException | NullPointerException e) { - // e.printStackTrace(); + // TODO JAL-3619 show error dialog / offer an alternative viewer + Cache.log.error( + "Invalid structure viewer type: " + type); } - } /** @@ -4569,17 +4657,28 @@ public class Jalview2XML String viewId, List autoAlan) { AlignFrame af = null; - af = new AlignFrame(al, view.getWidth(), view.getHeight(), - uniqueSeqSetId, viewId); + af = new AlignFrame(al, safeInt(view.getWidth()), + safeInt(view.getHeight()), uniqueSeqSetId, viewId) +// { +// +// @Override +// protected void processKeyEvent(java.awt.event.KeyEvent e) { +// System.out.println("Jalview2XML AF " + e); +// super.processKeyEvent(e); +// +// } +// +// } + ; af.setFileName(file, FileFormat.Jalview); final AlignViewport viewport = af.getViewport(); for (int i = 0; i < JSEQ.size(); i++) { - viewport.setSequenceColour( - viewport.getAlignment().getSequenceAt(i), - new java.awt.Color(JSEQ.get(i).getColour())); + int colour = safeInt(JSEQ.get(i).getColour()); + viewport.setSequenceColour(viewport.getAlignment().getSequenceAt(i), + new Color(colour)); } if (al.hasSeqrep()) @@ -4588,7 +4687,7 @@ public class Jalview2XML viewport.setDisplayReferenceSeq(true); } - viewport.setGatherViewsHere(view.isGatheredViews()); + viewport.setGatherViewsHere(safeBoolean(view.isGatheredViews())); if (view.getSequenceSetId() != null) { @@ -4640,49 +4739,48 @@ public class Jalview2XML } // recover view properties and display parameters - viewport.setShowAnnotation(view.isShowAnnotation()); - viewport.setAbovePIDThreshold(view.isPidSelected()); - final int pidThreshold = view.getPidThreshold() == null ? 0 - : view.getPidThreshold().intValue(); + viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation())); + viewport.setAbovePIDThreshold(safeBoolean(view.isPidSelected())); + final int pidThreshold = safeInt(view.getPidThreshold()); viewport.setThreshold(pidThreshold); - viewport.setColourText(view.isShowColourText()); + viewport.setColourText(safeBoolean(view.isShowColourText())); viewport - .setConservationSelected(view.isConservationSelected()); - viewport.setIncrement(view.getConsThreshold() == null ? 0 - : view.getConsThreshold().intValue()); - viewport.setShowJVSuffix(view.isShowFullId()); - viewport.setRightAlignIds(view.isRightAlignIds()); - viewport.setFont(new java.awt.Font(view.getFontName(), - view.getFontStyle(), view.getFontSize()), true); + .setConservationSelected( + safeBoolean(view.isConservationSelected())); + viewport.setIncrement(safeInt(view.getConsThreshold())); + viewport.setShowJVSuffix(safeBoolean(view.isShowFullId())); + viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds())); + viewport.setFont(new Font(view.getFontName(), + safeInt(view.getFontStyle()), safeInt(view.getFontSize())), + true); ViewStyleI vs = viewport.getViewStyle(); vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna()); viewport.setViewStyle(vs); // TODO: allow custom charWidth/Heights to be restored by updating them // after setting font - which means set above to false - viewport.setRenderGaps(view.isRenderGaps()); - viewport.setWrapAlignment(view.isWrapAlignment()); - viewport.setShowAnnotation(view.isShowAnnotation()); + viewport.setRenderGaps(safeBoolean(view.isRenderGaps())); + viewport.setWrapAlignment(safeBoolean(view.isWrapAlignment())); + viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation())); - viewport.setShowBoxes(view.isShowBoxes()); + viewport.setShowBoxes(safeBoolean(view.isShowBoxes())); - viewport.setShowText(view.isShowText()); + viewport.setShowText(safeBoolean(view.isShowText())); - viewport.setTextColour(new java.awt.Color(view.getTextCol1())); - viewport.setTextColour2(new java.awt.Color(view.getTextCol2())); - viewport.setThresholdTextColour(view.getTextColThreshold()); - viewport.setShowUnconserved( - view.isShowUnconserved() ? view.isShowUnconserved() : false); - viewport.getRanges().setStartRes(view.getStartRes()); + viewport.setTextColour(new Color(safeInt(view.getTextCol1()))); + viewport.setTextColour2(new Color(safeInt(view.getTextCol2()))); + viewport.setThresholdTextColour(safeInt(view.getTextColThreshold())); + viewport.setShowUnconserved(view.isShowUnconserved()); + viewport.getRanges().setStartRes(safeInt(view.getStartRes())); if (view.getViewName() != null) { viewport.setViewName(view.getViewName()); af.setInitialTabVisible(); } - af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(), - view.getHeight()); + af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()), + safeInt(view.getWidth()), safeInt(view.getHeight())); // startSeq set in af.alignPanel.updateLayout below af.alignPanel.updateLayout(); ColourSchemeI cs = null; @@ -4703,96 +4801,52 @@ public class Jalview2XML } else { - cs = ColourSchemeProperty.getColourScheme(al, view.getBgColour()); + cs = ColourSchemeProperty.getColourScheme(af.getViewport(), al, + view.getBgColour()); } } + /* + * turn off 'alignment colour applies to all groups' + * while restoring global colour scheme + */ + viewport.setColourAppliesToAllGroups(false); viewport.setGlobalColourScheme(cs); viewport.getResidueShading().setThreshold(pidThreshold, view.isIgnoreGapsinConsensus()); viewport.getResidueShading() .setConsensus(viewport.getSequenceConsensusHash()); - viewport.setColourAppliesToAllGroups(false); - - if (view.isConservationSelected() && cs != null) + if (safeBoolean(view.isConservationSelected()) && cs != null) { viewport.getResidueShading() - .setConservationInc(view.getConsThreshold()); + .setConservationInc(safeInt(view.getConsThreshold())); } - af.changeColour(cs); - viewport.setColourAppliesToAllGroups(true); viewport - .setShowSequenceFeatures(view.isShowSequenceFeatures()); + .setShowSequenceFeatures( + safeBoolean(view.isShowSequenceFeatures())); - // if (view.hasCentreColumnLabels()) - // { viewport.setCentreColumnLabels(view.isCentreColumnLabels()); - // } - // if (view.hasIgnoreGapsinConsensus()) - // { viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null); - // } - // if (view.hasFollowHighlight()) - // { - viewport.setFollowHighlight(view.isFollowHighlight()); - // } - // if (view.hasFollowSelection()) - // { viewport.followSelection = view.isFollowSelection(); - // } - // if (view.hasShowConsensusHistogram()) - // { viewport.setShowConsensusHistogram(view.isShowConsensusHistogram()); - // } - // else - // { - // viewport.setShowConsensusHistogram(true); - // } - // if (view.hasShowSequenceLogo()) - // { viewport.setShowSequenceLogo(view.isShowSequenceLogo()); - // } - // else - // { - // viewport.setShowSequenceLogo(false); - // } - // if (view.hasNormaliseSequenceLogo()) - // { viewport.setNormaliseSequenceLogo(view.isNormaliseSequenceLogo()); - // } - if (view.isShowDbRefTooltip() != null) - { - viewport.setShowDBRefs(view.isShowDbRefTooltip()); - } - if (view.isShowNPfeatureTooltip() != null) - { - viewport.setShowNPFeats(view.isShowNPfeatureTooltip()); - } - // if (view.hasShowGroupConsensus()) - // { + viewport.setShowDBRefs(safeBoolean(view.isShowDbRefTooltip())); + viewport.setShowNPFeats(safeBoolean(view.isShowNPfeatureTooltip())); viewport.setShowGroupConsensus(view.isShowGroupConsensus()); - // } - // else - // { - // viewport.setShowGroupConsensus(false); - // } - // if (view.hasShowGroupConservation()) - // { viewport.setShowGroupConservation(view.isShowGroupConservation()); - // } - // else - // { - // viewport.setShowGroupConservation(false); - // } + viewport.setShowComplementFeatures(view.isShowComplementFeatures()); + viewport.setShowComplementFeaturesOnTop( + view.isShowComplementFeaturesOnTop()); // recover feature settings if (jm.getFeatureSettings() != null) { - FeatureRenderer fr = af.alignPanel.getSeqPanel().seqCanvas + FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas .getFeatureRenderer(); FeaturesDisplayed fdi; viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed()); @@ -4843,13 +4897,11 @@ public class Jalview2XML { noValueColour = maxColour; } - float min = setting.getMin() != null - ? setting.getMin().floatValue() - : 0f; - float max = setting.getMin() != null - ? setting.getMax().floatValue() - : 1f; - FeatureColourI gc = new FeatureColour(minColour, maxColour, + float min = safeFloat(safeFloat(setting.getMin())); + float max = setting.getMax() == null ? 1f + : setting.getMax().floatValue(); + FeatureColourI gc = new FeatureColour(maxColour, minColour, + maxColour, noValueColour, min, max); if (setting.getAttributeName().size() > 0) { @@ -4859,7 +4911,7 @@ public class Jalview2XML if (setting.getThreshold() != null) { gc.setThreshold(setting.getThreshold().floatValue()); - int threshstate = setting.getThreshstate().intValue(); + int threshstate = safeInt(setting.getThreshstate()); // -1 = None, 0 = Below, 1 = Above threshold if (threshstate == 0) { @@ -4894,10 +4946,10 @@ public class Jalview2XML } else { - featureOrder.put(featureType, new Float( + featureOrder.put(featureType, Float.valueOf( fs / jm.getFeatureSettings().getSetting().size())); } - if (setting.isDisplay()) + if (safeBoolean(setting.isDisplay())) { fdi.setVisible(featureType); } @@ -4906,7 +4958,7 @@ public class Jalview2XML for (int gs = 0; gs < jm.getFeatureSettings().getGroup().size(); gs++) { Group grp = jm.getFeatureSettings().getGroup().get(gs); - fgtable.put(grp.getName(), new Boolean(grp.isDisplay())); + fgtable.put(grp.getName(), Boolean.valueOf(grp.isDisplay())); } // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder, // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ? @@ -4920,9 +4972,9 @@ public class Jalview2XML { for (int c = 0; c < view.getHiddenColumns().size(); c++) { - viewport.hideColumns(view.getHiddenColumns().get(c).getStart(), - view.getHiddenColumns().get(c).getEnd() // +1 - ); + final HiddenColumns hc = view.getHiddenColumns().get(c); + viewport.hideColumns(safeInt(hc.getStart()), + safeInt(hc.getEnd()) /* +1 */); } } if (view.getCalcIdParam() != null) @@ -4953,8 +5005,8 @@ public class Jalview2XML String complementaryViewId = view.getComplementId(); if (complementaryViewId == null) { - Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(), - view.getHeight()); + Desktop.addInternalFrame(af, view.getTitle(), + safeInt(view.getWidth()), safeInt(view.getHeight())); // recompute any autoannotation af.alignPanel.updateAnnotation(false, true); reorderAutoannotation(af, al, autoAlan); @@ -4964,6 +5016,7 @@ public class Jalview2XML { splitFrameCandidates.put(view, af); } + return af; } @@ -5029,34 +5082,36 @@ public class Jalview2XML } if (matchedAnnotation.getThreshold() == null) { - matchedAnnotation.setThreshold(new GraphLine( - viewAnnColour.getThreshold(), "Threshold", Color.black)); + matchedAnnotation.setThreshold( + new GraphLine(safeFloat(viewAnnColour.getThreshold()), + "Threshold", Color.black)); } AnnotationColourGradient cs = null; if (viewAnnColour.getColourScheme().equals("None")) { cs = new AnnotationColourGradient(matchedAnnotation, - new Color(viewAnnColour.getMinColour()), - new Color(viewAnnColour.getMaxColour()), - viewAnnColour.getAboveThreshold()); + new Color(safeInt(viewAnnColour.getMinColour())), + new Color(safeInt(viewAnnColour.getMaxColour())), + safeInt(viewAnnColour.getAboveThreshold())); } else if (viewAnnColour.getColourScheme().startsWith("ucs")) { cs = new AnnotationColourGradient(matchedAnnotation, getUserColourScheme(model, viewAnnColour.getColourScheme()), - viewAnnColour.getAboveThreshold()); + safeInt(viewAnnColour.getAboveThreshold())); } else { cs = new AnnotationColourGradient(matchedAnnotation, - ColourSchemeProperty.getColourScheme(al, + ColourSchemeProperty.getColourScheme(af.getViewport(), al, viewAnnColour.getColourScheme()), - viewAnnColour.getAboveThreshold()); + safeInt(viewAnnColour.getAboveThreshold())); } - boolean perSequenceOnly = viewAnnColour.isPerSequence(); - boolean useOriginalColours = viewAnnColour.isPredefinedColours(); + boolean perSequenceOnly = safeBoolean(viewAnnColour.isPerSequence()); + boolean useOriginalColours = safeBoolean( + viewAnnColour.isPredefinedColours()); cs.setSeqAssociated(perSequenceOnly); cs.setPredefinedColours(useOriginalColours); @@ -5073,7 +5128,7 @@ public class Jalview2XML AnnotationColourGradient groupScheme = new AnnotationColourGradient( matchedAnnotation, sg.getColourScheme(), - viewAnnColour.getAboveThreshold()); + safeInt(viewAnnColour.getAboveThreshold())); sg.setColourScheme(groupScheme); groupScheme.setSeqAssociated(perSequenceOnly); groupScheme.setPredefinedColours(useOriginalColours); @@ -5238,15 +5293,53 @@ public class Jalview2XML } private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al, - boolean ignoreUnrefed) + boolean ignoreUnrefed, String uniqueSeqSetId) { jalview.datamodel.AlignmentI ds = getDatasetFor( vamsasSet.getDatasetId()); - Vector dseqs = null; + AlignmentI xtant_ds = ds; + if (xtant_ds == null) + { + // good chance we are about to create a new dataset, but check if we've + // seen some of the dataset sequence IDs before. + // TODO: skip this check if we are working with project generated by + // version 2.11 or later + xtant_ds = checkIfHasDataset(vamsasSet.getSequence()); + if (xtant_ds != null) + { + ds = xtant_ds; + addDatasetRef(vamsasSet.getDatasetId(), ds); + } + } + Vector dseqs = null; + if (!ignoreUnrefed) + { + // recovering an alignment View + AlignmentI seqSetDS = getDatasetFor(UNIQSEQSETID + uniqueSeqSetId); + if (seqSetDS != null) + { + if (ds != null && ds != seqSetDS) + { + warn("JAL-3171 regression: Overwriting a dataset reference for an alignment" + + " - CDS/Protein crossreference data may be lost"); + if (xtant_ds != null) + { + // This can only happen if the unique sequence set ID was bound to a + // dataset that did not contain any of the sequences in the view + // currently being restored. + warn("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."); + } + } + ds = seqSetDS; + addDatasetRef(vamsasSet.getDatasetId(), ds); + } + } if (ds == null) { + // try even harder to restore dataset + AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence()); // create a list of new dataset sequences - dseqs = new Vector(); + dseqs = new Vector<>(); } for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++) { @@ -5267,10 +5360,58 @@ public class Jalview2XML if (al.getDataset() == null && !ignoreUnrefed) { al.setDataset(ds); + // register dataset for the alignment's uniqueSeqSetId for legacy projects + addDatasetRef(UNIQSEQSETID + uniqueSeqSetId, ds); } + updateSeqDatasetBinding(vamsasSet.getSequence(), ds); } /** + * XML dataset sequence ID to materialised dataset reference + */ + HashMap seqToDataset = new HashMap<>(); + + /** + * @return the first materialised dataset reference containing a dataset + * sequence referenced in the given view + * @param list + * - sequences from the view + */ + AlignmentI checkIfHasDataset(List list) + { + for (Sequence restoredSeq : list) + { + AlignmentI datasetFor = seqToDataset.get(restoredSeq.getDsseqid()); + if (datasetFor != null) + { + return datasetFor; + } + } + return null; + } + + /** + * Register ds as the containing dataset for the dataset sequences referenced + * by sequences in list + * + * @param list + * - sequences in a view + * @param ds + */ + void updateSeqDatasetBinding(List list, AlignmentI ds) + { + for (Sequence restoredSeq : list) + { + AlignmentI prevDS = seqToDataset.put(restoredSeq.getDsseqid(), ds); + if (prevDS != null && prevDS != ds) + { + warn("Dataset sequence appears in many datasets: " + + restoredSeq.getDsseqid()); + // TODO: try to merge! + } + } + } + /** * * @param vamsasSeq * sequence definition to create/merge dataset sequence for @@ -5286,7 +5427,8 @@ public class Jalview2XML * vamsasSeq array ordering, to preserve ordering of dataset */ private void ensureJalviewDatasetSequence(Sequence vamsasSeq, - AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos) + AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, + int vseqpos) { // JBP TODO: Check this is called for AlCodonFrames to support recovery of // xRef Codon Maps @@ -5491,13 +5633,29 @@ public class Jalview2XML return datasetId; } + /** + * Add any saved DBRefEntry's to the sequence. An entry flagged as 'locus' is + * constructed as a special subclass GeneLocus. + * + * @param datasetSequence + * @param sequence + */ private void addDBRefs(SequenceI datasetSequence, Sequence sequence) { for (int d = 0; d < sequence.getDBRef().size(); d++) { DBRef dr = sequence.getDBRef().get(d); - jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry( - dr.getSource(), dr.getVersion(), dr.getAccessionId()); + DBRefEntry entry; + if (dr.isLocus()) + { + entry = new GeneLocus(dr.getSource(), dr.getVersion(), + dr.getAccessionId()); + } + else + { + entry = new DBRefEntry(dr.getSource(), dr.getVersion(), + dr.getAccessionId()); + } if (dr.getMapping() != null) { entry.setMap(addMapping(dr.getMapping())); @@ -5529,15 +5687,16 @@ public class Jalview2XML jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto, fr, fto, m.getMapFromUnit().intValue(), m.getMapToUnit().intValue()); - // if (m.getMappingChoice() != null) - // { - // MappingChoice mc = m.getMappingChoice(); + + /* + * (optional) choice of dseqFor or Sequence + */ if (m.getDseqFor() != null) { String dsfor = m.getDseqFor(); if (seqRefIds.containsKey(dsfor)) { - /** + /* * recover from hash */ jmap.setTo(seqRefIds.get(dsfor)); @@ -5547,9 +5706,9 @@ public class Jalview2XML frefedSequence.add(newMappingRef(dsfor, jmap)); } } - else + else if (m.getSequence() != null) { - /** + /* * local sequence definition */ Sequence ms = m.getSequence(); @@ -5606,6 +5765,10 @@ public class Jalview2XML initSeqRefs(); JalviewModel jm = saveState(ap, null, null, null); + addDatasetRef( + jm.getVamsasModel().getSequenceSet().get(0).getDatasetId(), + ap.getAlignment().getDataset()); + uniqueSetSuffix = ""; // jm.getJalviewModelSequence().getViewport(0).setId(null); jm.getViewport().get(0).setId(null); @@ -5867,6 +6030,299 @@ public class Jalview2XML } /** + * Loads any saved PCA viewers + * + * @param jms + * @param ap + */ + protected void loadPCAViewers(JalviewModel model, AlignmentPanel ap) + { + try + { + List pcaviewers = model.getPcaViewer(); + for (PcaViewer viewer : pcaviewers) + { + String modelName = viewer.getScoreModelName(); + SimilarityParamsI params = new SimilarityParams( + viewer.isIncludeGappedColumns(), viewer.isMatchGaps(), + viewer.isIncludeGaps(), + viewer.isDenominateByShortestLength()); + + /* + * create the panel (without computing the PCA) + */ + PCAPanel panel = new PCAPanel(ap, modelName, params); + + panel.setTitle(viewer.getTitle()); + panel.setBounds(new Rectangle(viewer.getXpos(), viewer.getYpos(), + viewer.getWidth(), viewer.getHeight())); + + boolean showLabels = viewer.isShowLabels(); + panel.setShowLabels(showLabels); + panel.getRotatableCanvas().setShowLabels(showLabels); + panel.getRotatableCanvas() + .setBgColour(new Color(viewer.getBgColour())); + panel.getRotatableCanvas() + .setApplyToAllViews(viewer.isLinkToAllViews()); + + /* + * load PCA output data + */ + ScoreModelI scoreModel = ScoreModels.getInstance() + .getScoreModel(modelName, ap); + PCA pca = new PCA(null, scoreModel, params); + PcaDataType pcaData = viewer.getPcaData(); + + MatrixI pairwise = loadDoubleMatrix(pcaData.getPairwiseMatrix()); + pca.setPairwiseScores(pairwise); + + MatrixI triDiag = loadDoubleMatrix(pcaData.getTridiagonalMatrix()); + pca.setTridiagonal(triDiag); + + MatrixI result = loadDoubleMatrix(pcaData.getEigenMatrix()); + pca.setEigenmatrix(result); + + panel.getPcaModel().setPCA(pca); + + /* + * we haven't saved the input data! (JAL-2647 to do) + */ + panel.setInputData(null); + + /* + * add the sequence points for the PCA display + */ + List seqPoints = new ArrayList<>(); + for (SequencePoint sp : viewer.getSequencePoint()) + { + String seqId = sp.getSequenceRef(); + SequenceI seq = seqRefIds.get(seqId); + if (seq == null) + { + throw new IllegalStateException( + "Unmatched seqref for PCA: " + seqId); + } + Point pt = new Point(sp.getXPos(), sp.getYPos(), sp.getZPos()); + jalview.datamodel.SequencePoint seqPoint = new jalview.datamodel.SequencePoint( + seq, pt); + seqPoints.add(seqPoint); + } + panel.getRotatableCanvas().setPoints(seqPoints, seqPoints.size()); + + /* + * set min-max ranges and scale after setPoints (which recomputes them) + */ + panel.getRotatableCanvas().setScaleFactor(viewer.getScaleFactor()); + SeqPointMin spMin = viewer.getSeqPointMin(); + float[] min = new float[] { spMin.getXPos(), spMin.getYPos(), + spMin.getZPos() }; + SeqPointMax spMax = viewer.getSeqPointMax(); + float[] max = new float[] { spMax.getXPos(), spMax.getYPos(), + spMax.getZPos() }; + panel.getRotatableCanvas().setSeqMinMax(min, max); + + // todo: hold points list in PCAModel only + panel.getPcaModel().setSequencePoints(seqPoints); + + panel.setSelectedDimensionIndex(viewer.getXDim(), X); + panel.setSelectedDimensionIndex(viewer.getYDim(), Y); + panel.setSelectedDimensionIndex(viewer.getZDim(), Z); + + // is this duplication needed? + panel.setTop(seqPoints.size() - 1); + panel.getPcaModel().setTop(seqPoints.size() - 1); + + /* + * add the axes' end points for the display + */ + for (int i = 0; i < 3; i++) + { + Axis axis = viewer.getAxis().get(i); + panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point( + axis.getXPos(), axis.getYPos(), axis.getZPos()); + } + + Desktop.addInternalFrame(panel, MessageManager.formatMessage( + "label.calc_title", "PCA", modelName), 475, 450); + } + } catch (Exception ex) + { + Cache.log.error("Error loading PCA: " + ex.toString()); + } + } + + /** + * Creates a new structure viewer window + * + * @param viewerType + * @param viewerData + * @param af + * @param jprovider + */ + protected void createStructureViewer( + ViewerType viewerType, final Entry viewerData, + AlignFrame af, jarInputStreamProvider jprovider) + { + final StructureViewerModel viewerModel = viewerData.getValue(); + String sessionFilePath = null; + + if (viewerType == ViewerType.JMOL) + { + sessionFilePath = rewriteJmolSession(viewerModel, jprovider); + } + else + { + String viewerJarEntryName = getViewerJarEntryName( + viewerModel.getViewId()); + sessionFilePath = copyJarEntry(jprovider, + viewerJarEntryName, + "viewerSession", ".tmp"); + } + final String sessionPath = sessionFilePath; + final String sviewid = viewerData.getKey(); + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run() + { + JalviewStructureDisplayI sview = null; + try + { + sview = StructureViewer.createView(viewerType, af.alignPanel, + viewerModel, sessionPath, sviewid); + addNewStructureViewer(sview); + } catch (OutOfMemoryError ex) + { + new OOMWarning("Restoring structure view for " + + viewerType, + (OutOfMemoryError) ex.getCause()); + if (sview != null && sview.isVisible()) + { + sview.closeViewer(false); + sview.setVisible(false); + sview.dispose(); + } + } + } + }); + } catch (InvocationTargetException | InterruptedException ex) + { + warn("Unexpected error when opening " + viewerType + + " structure viewer", ex); + } + } + + /** + * Rewrites a Jmol session script, saves it to a temporary file, and returns + * the path of the file. "load file" commands are rewritten to change the + * original PDB file names to those created as the Jalview project is loaded. + * + * @param svattrib + * @param jprovider + * @return + */ + private String rewriteJmolSession(StructureViewerModel svattrib, + jarInputStreamProvider jprovider) + { + String state = svattrib.getStateData(); // Jalview < 2.9 + if (state == null || state.isEmpty()) // Jalview >= 2.9 + { + String jarEntryName = getViewerJarEntryName(svattrib.getViewId()); + state = readJarEntry(jprovider, jarEntryName); + } + // TODO or simpler? for each key in oldFiles, + // replace key.getPath() in state with oldFiles.get(key).getFilePath() + // (allowing for different path escapings) + StringBuilder rewritten = new StringBuilder(state.length()); + int cp = 0, ncp, ecp; + Map oldFiles = svattrib.getFileData(); + while ((ncp = state.indexOf("load ", cp)) > -1) + { + do + { + // look for next filename in load statement + rewritten.append(state.substring(cp, + ncp = (state.indexOf("\"", ncp + 1) + 1))); + String oldfilenam = state.substring(ncp, + ecp = state.indexOf("\"", ncp)); + // recover the new mapping data for this old filename + // have to normalize filename - since Jmol and jalview do + // filename translation differently. + StructureData filedat = oldFiles.get(new File(oldfilenam)); + if (filedat == null) + { + String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\"); + filedat = oldFiles.get(new File(reformatedOldFilename)); + } + rewritten + .append(Platform.escapeBackslashes(filedat.getFilePath())); + rewritten.append("\""); + cp = ecp + 1; // advance beyond last \" and set cursor so we can + // look for next file statement. + } while ((ncp = state.indexOf("/*file*/", cp)) > -1); + } + if (cp > 0) + { + // just append rest of state + rewritten.append(state.substring(cp)); + } + else + { + System.err.print("Ignoring incomplete Jmol state for PDB ids: "); + rewritten = new StringBuilder(state); + rewritten.append("; load append "); + for (File id : oldFiles.keySet()) + { + // add pdb files that should be present in the viewer + StructureData filedat = oldFiles.get(id); + rewritten.append(" \"").append(filedat.getFilePath()).append("\""); + } + rewritten.append(";"); + } + + if (rewritten.length() == 0) + { + return null; + } + final String history = "history = "; + int historyIndex = rewritten.indexOf(history); + if (historyIndex > -1) + { + /* + * change "history = [true|false];" to "history = [1|0];" + */ + historyIndex += history.length(); + String val = rewritten.substring(historyIndex, historyIndex + 5); + if (val.startsWith("true")) + { + rewritten.replace(historyIndex, historyIndex + 4, "1"); + } + else if (val.startsWith("false")) + { + rewritten.replace(historyIndex, historyIndex + 5, "0"); + } + } + + try + { + File tmp = File.createTempFile("viewerSession", ".tmp"); + try (OutputStream os = new FileOutputStream(tmp)) + { + InputStream is = new ByteArrayInputStream( + rewritten.toString().getBytes()); + copyAll(is, os); + return tmp.getAbsolutePath(); + } + } catch (IOException e) + { + Cache.log.error("Error restoring Jmol session: " + e.toString()); + } + return null; + } + + /** * Populates an XML model of the feature colour scheme for one feature type * * @param featureType @@ -6129,9 +6585,9 @@ public class Jalview2XML noValueColour = maxcol; } - colour = new FeatureColour(mincol, maxcol, noValueColour, - colourModel.getMin(), - colourModel.getMax()); + colour = new FeatureColour(maxcol, mincol, maxcol, noValueColour, + safeFloat(colourModel.getMin()), + safeFloat(colourModel.getMax())); final List attributeName = colourModel.getAttributeName(); String[] attributes = attributeName .toArray(new String[attributeName.size()]);