X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FVamsasApplication.java;h=2879c2faa71d73fb47d42ed94589101a44b3e059;hb=85c063b49c067bbb850941da79a796cbd5ef032f;hp=65212a682b4877442a470f53f2122c418afcab78;hpb=9ca31f2261760b6fad088295feb701e400e7150f;p=jalview.git diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java index 65212a6..2879c2f 100644 --- a/src/jalview/gui/VamsasApplication.java +++ b/src/jalview/gui/VamsasApplication.java @@ -1,11 +1,31 @@ -/** - * +/* + * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1) + * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package jalview.gui; import jalview.bin.Cache; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.io.VamsasAppDatastore; +import jalview.structure.SelectionListener; +import jalview.structure.SelectionSource; import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasListener; @@ -13,8 +33,10 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; +import java.util.Enumeration; import java.util.Hashtable; import java.util.IdentityHashMap; +import java.util.Iterator; import javax.swing.JInternalFrame; import javax.swing.JOptionPane; @@ -30,13 +52,17 @@ import uk.ac.vamsas.client.picking.IMessageHandler; import uk.ac.vamsas.client.picking.IPickManager; import uk.ac.vamsas.client.picking.Message; import uk.ac.vamsas.client.picking.MouseOverMessage; +import uk.ac.vamsas.client.picking.SelectionMessage; import uk.ac.vamsas.objects.core.Entry; +import uk.ac.vamsas.objects.core.Input; +import uk.ac.vamsas.objects.core.Pos; +import uk.ac.vamsas.objects.core.Seg; /** * @author jimp * */ -public class VamsasApplication +public class VamsasApplication implements SelectionSource { IClient vclient = null; @@ -89,7 +115,7 @@ public class VamsasApplication /** * @throws IOException - * or other if clientfactory instantiation failed. + * or other if clientfactory instantiation failed. * @return list of current sessions or null if no session exists. */ public static String[] getSessionList() throws Exception @@ -102,9 +128,9 @@ public class VamsasApplication * session * * @param sess - * null or a valid session url + * null or a valid session url * @param vamsasDocument - * null or a valid vamsas document file + * null or a valid vamsas document file * @return false if no vamsas connection was made */ private boolean initClientSession(String sess, File vamsasDocument) @@ -149,6 +175,8 @@ public class VamsasApplication vclient = clientfactory.getIClient(app, sess); } } + // set some properties for our VAMSAS interaction + setVclientConfig(); user = vclient.getUserHandle(); } catch (Exception e) @@ -160,6 +188,36 @@ public class VamsasApplication return true; } + private void setVclientConfig() + { + if (vclient == null) + { + return; + } + try + { + if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient) + { + uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient) vclient) + .getSimpleClientConfig(); + cfg._validatemergedroots = false; + cfg._validateupdatedroots = true; // we may write rubbish otherwise. + } + } catch (Error e) + { + Cache.log + .warn( + "Probable SERIOUS VAMSAS client incompatibility - carrying on regardless", + e); + } catch (Exception e) + { + Cache.log + .warn( + "Probable VAMSAS client incompatibility - carrying on regardless", + e); + } + } + /** * make the appHandle for Jalview * @@ -192,11 +250,12 @@ public class VamsasApplication "Impementation error! Vamsas Operations when client not initialised and connected."); } addDocumentUpdateHandler(); + addStoreDocumentHandler(); startSession(); Cache.log .debug("Jalview loading the Vamsas Session for the first time."); dealWithDocumentUpdate(false); // we don't push an update out to the - // document yet. + // document yet. Cache.log.debug("... finished update for the first time."); } @@ -236,21 +295,40 @@ public class VamsasApplication public void push_update() { - Cache.log.info("Jalview updating to the Vamsas Session."); - dealWithDocumentUpdate(true); - /* - * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); } - * catch (Exception e) { Cache.log.error("Failed to get client document for - * update."); // RAISE A WARNING DIALOG disableGui(false); return; } - * updateVamsasDocument(cdoc); updateJalviewGui(); - * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags - * back vclient.updateDocument(cdoc); - */ - Cache.log.info("Jalview finished updating to the Vamsas Session."); + Thread udthread = new Thread(new Runnable() + { + + public void run() + { + Cache.log.info("Jalview updating to the Vamsas Session."); + + dealWithDocumentUpdate(true); + Cache.log.info("Jalview finished updating to the Vamsas Session."); + } + + }); + udthread.start(); } + /** + * leave a session, prompting the user to save if necessary + */ public void end_session() { + end_session(true); + } + + private boolean promptUser = true; + + /** + * leave a session, optionally prompting the user to save if necessary + * + * @param promptUser + * when true enable prompting by this application + */ + + public void end_session(boolean promptUser) + { if (!inSession()) throw new Error("Jalview not connected to Vamsas session."); Cache.log.info("Jalview disconnecting from the Vamsas Session."); @@ -258,8 +336,11 @@ public class VamsasApplication { if (joinedSession) { + boolean ourprompt = this.promptUser; + this.promptUser = promptUser; vclient.finalizeClient(); Cache.log.info("Jalview has left the session."); + this.promptUser = ourprompt; // restore default value } else { @@ -283,18 +364,41 @@ public class VamsasApplication Cache.log.debug("Jalview updating from sesion document .."); ensureJvVamsas(); VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj, - baseProvEntry()); - vds.updateToJalview(); + baseProvEntry(), alRedoState); + try + { + vds.updateToJalview(); + } catch (Exception e) + { + Cache.log.error("Failed to update Jalview from vamsas document.", e); + } + try + { + if (firstUpdate) + { + vds.updateJalviewFromAppdata(); + // Comment this out to repeatedly read in data from JalviewAppData + // firstUpdate=false; + } + } catch (Exception e) + { + Cache.log.error( + "Exception when updating Jalview settings from Appdata.", e); + } Cache.log.debug(".. finished updating from sesion document."); } + boolean firstUpdate = false; + private void ensureJvVamsas() { if (jv2vobj == null) { jv2vobj = new IdentityHashMap(); vobj2jv = new Hashtable(); + alRedoState = new Hashtable(); + firstUpdate = true; } } @@ -305,11 +409,26 @@ public class VamsasApplication Hashtable vobj2jv = null; + Hashtable alRedoState = null; + + boolean errorsDuringUpdate = false; + + boolean errorsDuringAppUpdate = false; + + /** + * update the document accessed through doc. A backup of the current object + * bindings is made. + * + * @param doc + */ public void updateVamsasDocument(IClientDocument doc) { ensureJvVamsas(); + errorsDuringUpdate = false; + errorsDuringAppUpdate = false; + backup_objectMapping(); VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj, - baseProvEntry()); + baseProvEntry(), alRedoState); // wander through frames JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -317,6 +436,8 @@ public class VamsasApplication { return; } + Hashtable skipList = new Hashtable(); + Hashtable viewset = new Hashtable(); try { @@ -326,26 +447,75 @@ public class VamsasApplication if (frames[i] instanceof AlignFrame) { AlignFrame af = (AlignFrame) frames[i]; - - // update alignment and root from frame. - vds.storeVAMSAS(af.getViewport(), af.getTitle()); + if (!viewset.containsKey(af.getViewport().getSequenceSetId())) + { + // update alignment and root from frame. + boolean stored = false; + try + { + stored = vds.storeVAMSAS(af.getViewport(), af.getTitle()); + } catch (Exception e) + { + errorsDuringUpdate = true; + Cache.log.error("Exception synchronizing " + + af.getTitle() + + " " + + (af.getViewport().viewName == null ? "" : " view " + + af.getViewport().viewName) + + " to document.", e); + stored = false; + } + if (!stored) + { // record skip in skipList + skipList.put(af.getViewport().getSequenceSetId(), af); + } + else + { + // could try to eliminate sequenceSetId from skiplist .. + // (skipList.containsKey(af.getViewport().getSequenceSetId())) + // remember sequenceSetId so we can skip all the other views on + // same alignment + viewset.put(af.getViewport().getSequenceSetId(), af); + } + } } } // REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) + // for (int i = frames.length - 1; i > -1; i--) + // { + // if (frames[i] instanceof AlignFrame) + // { + // AlignFrame af = (AlignFrame) frames[i]; + Iterator aframes = viewset.values().iterator(); + while (aframes.hasNext()) { - if (frames[i] instanceof AlignFrame) - { - AlignFrame af = (AlignFrame) frames[i]; + AlignFrame af = (AlignFrame) aframes.next(); + // add any AlignedCodonFrame mappings on this alignment to any other. + vds.storeSequenceMappings(af.getViewport(), af.getTitle()); + } + } catch (Exception e) + { + Cache.log.error("Exception synchronizing Views to Document :", e); + errorsDuringUpdate = true; + } - // add any AlignedCodonFrame mappings on this alignment to any other. - vds.storeSequenceMappings(af.getViewport(), af.getTitle()); - } + try + { + if (viewset.size() > 0) + { + // Alignment views were synchronized, so store their state in the + // appData, too. + // The skipList ensures we don't write out any alignments not actually + // in the document. + vds.setSkipList(skipList); + vds.updateJalviewClientAppdata(); } } catch (Exception e) { - Cache.log.error("Vamsas Document store exception", e); + Cache.log.error("Client Appdata Write exception", e); + errorsDuringAppUpdate = true; } + vds.clearSkipList(); } private Entry baseProvEntry() @@ -362,7 +532,7 @@ public class VamsasApplication * do a vamsas document update or update jalview from the vamsas document * * @param fromJalview - * true to update from jalview to the vamsas document + * true to update from jalview to the vamsas document */ protected void dealWithDocumentUpdate(boolean fromJalview) { @@ -421,6 +591,9 @@ public class VamsasApplication { System.err.println("Exception whilst updating :"); ee.printStackTrace(System.err); + // recover object map backup, since its probably corrupted with references + // to Vobjects that don't exist anymore. + this.recover_objectMappingBackup(); } Cache.log.debug("Finished updating from document change."); disableGui(false); @@ -441,15 +614,100 @@ public class VamsasApplication Cache.log.debug("Added Jalview handler for vamsas document updates."); } + private void addStoreDocumentHandler() + { + final VamsasApplication client = this; + vclient.addVorbaEventHandler( + uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE, + new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent evt) + { + if (client.promptUser) + { + Cache.log + .debug("Asking user if the vamsas session should be stored."); + int reply = JOptionPane + .showInternalConfirmDialog( + Desktop.desktop, + "The current VAMSAS session has unsaved data - do you want to save it ?", + "VAMSAS Session Shutdown", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + + if (reply == JOptionPane.YES_OPTION) + { + Cache.log.debug("Prompting for vamsas store filename."); + Desktop.instance.vamsasSave_actionPerformed(null); + Cache.log + .debug("Finished attempt at storing document."); + } + Cache.log + .debug("finished dealing with REQUESTTOCLOSE event."); + } + else + { + Cache.log + .debug("Ignoring store document request (promptUser==false)"); + } + } + }); + Cache.log.debug("Added Jalview handler for vamsas document updates."); + } + public void disableGui(boolean b) { Desktop.instance.setVamsasUpdate(b); } + Hashtable _backup_vobj2jv; + + IdentityHashMap _backup_jv2vobj; + + /** + * make a backup of the object mappings (vobj2jv and jv2vobj) + */ + public void backup_objectMapping() + { + _backup_vobj2jv = new Hashtable(vobj2jv); + _backup_jv2vobj = new IdentityHashMap(jv2vobj); + } + + /** + * recover original object mappings from the object mapping backup if document + * IO failed + * + * @throws Error + * if backup_objectMapping was not called. + */ + public void recover_objectMappingBackup() + { + if (_backup_vobj2jv == null) + { + throw new Error( + "IMPLEMENTATION ERROR: Cannot recover vamsas object mappings - no backup was made."); + } + jv2vobj.clear(); + Iterator el = _backup_jv2vobj.entrySet().iterator(); + while (el.hasNext()) + { + java.util.Map.Entry mp = (java.util.Map.Entry) el.next(); + jv2vobj.put(mp.getKey(), mp.getValue()); + } + el = _backup_vobj2jv.entrySet().iterator(); + while (el.hasNext()) + { + java.util.Map.Entry mp = (java.util.Map.Entry) el.next(); + vobj2jv.put(mp.getKey(), mp.getValue()); + } + } + private boolean joinedSession = false; private VamsasListener picker = null; + private SelectionListener selecter; + private void startSession() { if (inSession()) @@ -469,13 +727,19 @@ public class VamsasApplication final IPickManager pm = vclient.getPickManager(); final StructureSelectionManager ssm = StructureSelectionManager .getStructureSelectionManager(); + final SelectionSource me = this; pm.registerMessageHandler(new IMessageHandler() { String last = null; public void handleMessage(Message message) { - if (message instanceof MouseOverMessage && vobj2jv != null) + if (vobj2jv == null) + { + // we are not in a session yet. + return; + } + if (message instanceof MouseOverMessage) { MouseOverMessage mm = (MouseOverMessage) message; String mstring = mm.getVorbaID() + " " + mm.getPosition(); @@ -494,11 +758,182 @@ public class VamsasApplication last = mstring; // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+" // bound to "+jvobj+" at "+mm.getPosition()); - // position is in sequence or in aligned sequence ??????? + // position is character position in aligned sequence ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm .getPosition()); } } + if (message instanceof uk.ac.vamsas.client.picking.SelectionMessage) + { + // we only care about AlignmentSequence selections + SelectionMessage sm = (SelectionMessage) message; + sm.validate(); + System.err.println("Received\n" + sm.getRawMessage()); + Object[] jvobjs = sm.getVorbaIDs() == null ? null + : new Object[sm.getVorbaIDs().length]; + if (jvobjs == null) + { + // TODO: rationalise : can only clear a selection over a + // referred to object + ssm.sendSelection(null, null, me); + return; + } + Class type = null; + boolean send = true; + for (int o = 0; o < jvobjs.length; o++) + { + jvobjs[o] = vobj2jv.get(sm.getVorbaIDs()[o]); + if (jvobjs[o] == null) + { + // can't cope with selections for unmapped objects + // continue; + } + if (type == null) + { + type = jvobjs[o].getClass(); + } + ; + if (type != jvobjs[o].getClass()) + { + send = false; + // discard - can't cope with selections over mixed objects + // continue; + } + } + SequenceGroup jselection = null; + ColumnSelection colsel = null; + if (type == jalview.datamodel.Alignment.class) + { + if (jvobjs.length == 1) + { + // TODO if (sm.isNone())// send a message to select the + // specified columns over the + // given + // alignment + + send = true; + } + } + if (type == jalview.datamodel.Sequence.class) + { + + SequenceI seq; + boolean aligned = ((jalview.datamodel.Sequence) jvobjs[0]) + .getDatasetSequence() != null; + int maxWidth = 0; + if (aligned) + { + jselection = new SequenceGroup(); + jselection.addSequence( + seq = (jalview.datamodel.Sequence) jvobjs[0], + false); + maxWidth = seq.getLength(); + } + for (int c = 1; aligned && jvobjs.length > 1 + && c < jvobjs.length; c++) + { + if (((jalview.datamodel.Sequence) jvobjs[c]) + .getDatasetSequence() == null) + { + aligned = false; + continue; + } + else + { + jselection.addSequence( + seq = (jalview.datamodel.Sequence) jvobjs[c], + false); + if (maxWidth < seq.getLength()) + { + maxWidth = seq.getLength(); + } + + } + } + if (!aligned) + { + jselection = null; + // if cardinality is greater than one then verify all + // sequences are alignment sequences. + if (jvobjs.length == 1) + { + // find all instances of this dataset sequence in the + // displayed alignments containing the associated range and + // select them. + } + } + else + { + jselection.setStartRes(0); + jselection.setEndRes(maxWidth); + // locate the alignment containing the given sequences and + // select the associated ranges on them. + if (sm.getRanges() != null) + { + int[] prange = uk.ac.vamsas.objects.utils.Range + .getBounds(sm.getRanges()); + boolean rangeset = false; + colsel = new ColumnSelection(); + prange = uk.ac.vamsas.objects.utils.Range + .getIntervals(sm.getRanges()); + for (int p = 0; p < prange.length; p += 2) + { + int d = (prange[p] <= prange[p + 1]) ? 1 : -1; + if (!rangeset) + { + if (jselection != null) + { + // set the bounds of the selected area using the first + // interval. + jselection.setStartRes(prange[p] - 1); + jselection.setEndRes(prange[p + 1] - 1); + rangeset = true; + } + } + else + { + // try to join up adjacent columns to make a larger + // selection + // lower and upper bounds + int l = (d < 0) ? 1 : 0; + int u = (d > 0) ? 1 : 0; + + if (jselection.getStartRes() > 0 + && prange[p + l] == jselection + .getStartRes()) + { + jselection.setStartRes(prange[p + l] - 1); + } + if (jselection.getEndRes() <= maxWidth + && prange[p + u] == (jselection.getEndRes() + 2)) + { + jselection.setEndRes(prange[p + u] - 1); + } + } + // mark all the columns in the range. + for (int sr = prange[p], er = prange[p + 1], de = er + + d; sr != de; sr += d) + { + colsel.addElement(sr - 1); + } + } + } + send = true; + } + } + if (send) + { + ssm.sendSelection(jselection, colsel, me); + } + // discard message. + for (int c = 0; c < jvobjs.length; c++) + { + jvobjs[c] = null; + } + ; + jvobjs = null; + return; + } } }); picker = new VamsasListener() @@ -516,8 +951,9 @@ public class VamsasApplication VorbaId v = (VorbaId) jv2vobj.get(seq); if (v != null) { - Cache.log.debug("Mouse over " + v.getId() + " bound to " - + seq + " at " + index); + // this should really be a trace message. + // Cache.log.debug("Mouse over " + v.getId() + " bound to " + // + seq + " at " + index); last = seq; i = index; MouseOverMessage message = new MouseOverMessage(v.getId(), @@ -527,11 +963,136 @@ public class VamsasApplication } } }; + selecter = new SelectionListener() + { + + public void selection(SequenceGroup seqsel, + ColumnSelection colsel, SelectionSource source) + { + if (vobj2jv == null) + { + Cache.log + .warn("Selection listener still active for dead session."); + // not in a session. + return; + } + if (source != me) + { + AlignmentI visal = null; + if (source instanceof AlignViewport) + { + visal = ((AlignViewport) source).getAlignment(); + } + SelectionMessage sm = null; + if ((seqsel == null || seqsel.getSize() == 0) + && (colsel == null || colsel.getSelected() == null || colsel + .getSelected().size() == 0)) + { + if (source instanceof AlignViewport) + { + // the empty selection. + sm = new SelectionMessage("jalview", new String[] + { ((AlignViewport) source).getSequenceSetId() }, null, + true); + } + else + { + // the empty selection. + sm = new SelectionMessage("jalview", null, null, true); + } + } + else + { + String[] vobj = new String[seqsel.getSize()]; + int o = 0; + Enumeration sels = seqsel.getSequences(null).elements(); + while (sels.hasMoreElements()) + { + SequenceI sel = (SequenceI) sels.nextElement(); + VorbaId v = (VorbaId) jv2vobj.get(sel); + if (v != null) + { + vobj[o++] = v.toString(); + } + } + if (o < vobj.length) + { + String t[] = vobj; + vobj = new String[o]; + System.arraycopy(t, 0, vobj, 0, o); + t = null; + } + Input range = null; + if (seqsel != null && colsel != null) + { + // deparse the colsel into positions on the vamsas alignment + // sequences + range = new Input(); + if (colsel.getSelected() != null + && colsel.getSelected().size() > 0 + && visal != null + && seqsel.getSize() == visal.getHeight()) + { + // gather selected columns outwith the sequence positions + // too + Enumeration cols = colsel.getSelected().elements(); + while (cols.hasMoreElements()) + { + int ival = ((Integer) cols.nextElement()).intValue(); + Pos p = new Pos(); + p.setI(ival + 1); + range.addPos(p); + } + } + else + { + int[] intervals = colsel.getVisibleContigs(seqsel + .getStartRes(), seqsel.getEndRes() + 1); + for (int iv = 0; iv < intervals.length; iv += 2) + { + Seg s = new Seg(); + s.setStart(intervals[iv] + 1); // vamsas indices begin at + // 1, not zero. + s.setEnd(intervals[iv + 1] + 1); + s.setInclusive(true); + range.addSeg(s); + } + } + } + if (vobj.length > 0) + { + sm = new SelectionMessage("jalview", vobj, range); + } + else + { + sm = null; + } + } + if (sm != null) + { + sm.validate(); // debug + Cache.log.debug("Selection Message\n" + sm.getRawMessage()); + pm.sendMessage(sm); + } + } + } + + }; ssm.addStructureViewerListener(picker); // better method here + ssm.addSelectionListener(selecter); } catch (Exception e) { Cache.log.error("Failed to init Vamsas Picking", e); } } } + + public String getCurrentSession() + { + if (vclient != null) + { + return (vclient.getSessionUrn()); + } + return null; + } }