JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / io / vamsas / Tree.java
index de8230c..a3781a7 100644 (file)
-package jalview.io.vamsas;\r
-\r
-import java.io.IOException;\r
-import java.util.Enumeration;\r
-import java.util.Hashtable;\r
-import java.util.Vector;\r
-\r
-import jalview.analysis.NJTree;\r
-import jalview.analysis.SequenceIdMatcher;\r
-import jalview.bin.Cache;\r
-import jalview.datamodel.AlignmentI;\r
-import jalview.datamodel.AlignmentView;\r
-import jalview.datamodel.BinaryNode;\r
-import jalview.datamodel.SeqCigar;\r
-import jalview.datamodel.Sequence;\r
-import jalview.datamodel.SequenceI;\r
-import jalview.datamodel.SequenceNode;\r
-import jalview.gui.AlignViewport;\r
-import jalview.gui.TreePanel;\r
-import jalview.io.NewickFile;\r
-import jalview.io.VamsasAppDatastore;\r
-import uk.ac.vamsas.client.Vobject;\r
-import uk.ac.vamsas.objects.core.AlignmentSequence;\r
-import uk.ac.vamsas.objects.core.Entry;\r
-import uk.ac.vamsas.objects.core.Input;\r
-import uk.ac.vamsas.objects.core.Newick;\r
-import uk.ac.vamsas.objects.core.Param;\r
-import uk.ac.vamsas.objects.core.Provenance;\r
-import uk.ac.vamsas.objects.core.Seg;\r
-import uk.ac.vamsas.objects.core.Treenode;\r
-import uk.ac.vamsas.objects.core.Vref;\r
-\r
-public class Tree extends DatastoreItem\r
-{\r
-  AlignmentI jal;\r
-  TreePanel tp;\r
-  uk.ac.vamsas.objects.core.Tree tree;\r
-  uk.ac.vamsas.objects.core.Alignment alignment; // may be null => dataset or\r
-                                                  // other kind of tree\r
-  private NewickFile ntree;\r
-  private String title;\r
-  private AlignmentView inputData = null;\r
-  public static void updateFrom(VamsasAppDatastore datastore, jalview.gui.AlignFrame alignFrame, uk.ac.vamsas.objects.core.Tree vtree) {\r
-    Tree toTree = new Tree(datastore, alignFrame, vtree);\r
-    \r
-  }\r
-  public Tree(VamsasAppDatastore datastore, jalview.gui.AlignFrame alignFrame, uk.ac.vamsas.objects.core.Tree vtree)\r
-  {\r
-    super(datastore);\r
-    tree = vtree;\r
-    TreePanel tp = (TreePanel) getvObj2jv(tree);\r
-    if (tp != null) {\r
-      if (tree.isUpdated())\r
-      {\r
-        Cache.log.info(\r
-                "Update from vamsas document to alignment associated tree not implemented yet.");\r
-      }\r
-    }\r
-    else\r
-    {\r
-      // make a new tree\r
-      Object[] idata = this.recoverInputData(tree.getProvenance());\r
-      try\r
-      {\r
-        if (idata != null && idata[0] != null)\r
-        {\r
-          inputData = (AlignmentView) idata[0];\r
-        }\r
-        ntree = getNtree();\r
-        title = tree.getNewick(0).getTitle();\r
-        if (title==null || title.length()==0)\r
-        {\r
-          title = tree.getTitle(); // hack!!!!\r
-        }\r
-      }\r
-      catch (Exception e)\r
-      {\r
-        Cache.log.warn("Problems parsing treefile '" +\r
-                       tree.getNewick(0).getContent() + "'", e);\r
-      }\r
-    }\r
-  }\r
-  private NewickFile getNtree() throws IOException\r
-  {\r
-    return new jalview.io.NewickFile(tree.getNewick(0).getContent());\r
-  }\r
-  public Tree(VamsasAppDatastore datastore, TreePanel tp2, AlignmentI jal2, uk.ac.vamsas.objects.core.Alignment alignment2)\r
-  {\r
-    super(datastore);\r
-\r
-    jal = jal2;\r
-    tp = tp2;\r
-    alignment = alignment2;\r
-    \r
-    tree = (uk.ac.vamsas.objects.core.Tree) getjv2vObj(tp);\r
-    if (tree == null)\r
-    {\r
-      add();\r
-    }\r
-    else\r
-    {\r
-      if (isModifiable(tree.getModifiable()))\r
-      {\r
-        // synchronize(); // update();\r
-        // verify any changes.\r
-        System.out.println("Update tree in document.");\r
-      }\r
-      else\r
-      {\r
-        // handle conflict\r
-        System.out\r
-            .println("Add modified tree as new tree in document.");\r
-      }\r
-    }\r
-  }\r
-  /**\r
-   * correctly creates provenance for trees calculated on an alignment by\r
-   * jalview.\r
-   * \r
-   * @param jal\r
-   * @param tp\r
-   * @return\r
-   */\r
-  private Provenance makeTreeProvenance(AlignmentI jal, TreePanel tp)\r
-  {\r
-    Cache.log.debug("Making Tree provenance for "+tp.getTitle());\r
-    Provenance prov = new Provenance();\r
-    prov.addEntry(new Entry());\r
-    prov.getEntry(0).setAction("imported " + tp.getTitle());\r
-    prov.getEntry(0).setUser(provEntry.getUser());\r
-    prov.getEntry(0).setApp(provEntry.getApp());\r
-    prov.getEntry(0).setDate(provEntry.getDate());\r
-    if (tp.getTree().hasOriginalSequenceData())\r
-    {\r
-      Input vInput = new Input();\r
-      // LATER: check to see if tree input data is contained in this alignment -\r
-      // or just correctly resolve the tree's seqData to the correct alignment\r
-      // in\r
-      // the document.\r
-      Vector alsqrefs = getjv2vObjs(findAlignmentSequences(jal, tp.getTree().seqData.getSequences()));\r
-      Object[] alsqs = new Object[alsqrefs.size()];\r
-      alsqrefs.copyInto(alsqs);\r
-      vInput.setObjRef(alsqs);\r
-      // now create main provenance data\r
-      prov.getEntry(0).setAction("created " + tp.getTitle());\r
-      prov.getEntry(0).addInput(vInput);\r
-      // jalview's special input parameter for distance matrix calculations\r
-      vInput.setName("jalview:seqdist"); // TODO: settle on appropriate name.\r
-      prov.getEntry(0).addParam(new Param());\r
-      prov.getEntry(0).getParam(0).setName("treeType");\r
-      prov.getEntry(0).getParam(0).setType("utf8");\r
-      prov.getEntry(0).getParam(0).setContent("NJ"); // TODO: type of tree is a general parameter\r
-      int ranges[] = tp.getTree().seqData.getVisibleContigs();\r
-      // VisibleContigs are with respect to alignment coordinates. Still need\r
-      // offsets\r
-      int start = tp.getTree().seqData.getAlignmentOrigin();\r
-      for (int r = 0; r < ranges.length; r += 2)\r
-      {\r
-        Seg visSeg = new Seg();\r
-        visSeg.setStart(1 + start + ranges[r]);\r
-        visSeg.setEnd(start + ranges[r + 1]);\r
-        visSeg.setInclusive(true);\r
-        vInput.addSeg(visSeg);\r
-      }\r
-    }\r
-    Cache.log.debug("Finished Tree provenance for "+tp.getTitle());\r
-    return prov;\r
-  }\r
-  /**\r
-   * look up SeqCigars in an existing alignment.\r
-   * \r
-   * @param jal\r
-   * @param sequences\r
-   * @return vector of alignment sequences in order of SeqCigar array (but\r
-   *         missing unfound seqcigars)\r
-   */\r
-  private Vector findAlignmentSequences(AlignmentI jal, SeqCigar[] sequences)\r
-  {\r
-    SeqCigar[] tseqs = new SeqCigar[sequences.length];\r
-    System.arraycopy(sequences, 0, tseqs, 0, sequences.length);\r
-    Vector alsq = new Vector();\r
-    Enumeration as = jal.getSequences().elements();\r
-    while (as.hasMoreElements())\r
-    {\r
-      SequenceI asq = (SequenceI) as.nextElement();\r
-      for (int t = 0; t<sequences.length; t++)\r
-      {\r
-        if (tseqs[t]!=null \r
-                && (tseqs[t].getRefSeq()==asq || tseqs[t].getRefSeq() == asq.getDatasetSequence()))\r
-                // && tseqs[t].getStart()>=asq.getStart() && tseqs[t].getEnd()<=asq.getEnd())\r
-        {\r
-          tseqs[t] = null;\r
-          alsq.add(asq);\r
-        }\r
-      }\r
-    }\r
-    if (alsq.size()<sequences.length)\r
-      Cache.log.warn("Not recovered all alignment sequences for given set of input sequence CIGARS");\r
-    return alsq;\r
-  }\r
-  /**\r
-   *\r
-   * Update jalview newick representation with TreeNode map\r
-\r
-   * @param tp the treepanel that this tree is bound to.\r
-   */\r
- public void UpdateSequenceTreeMap(TreePanel tp)\r
- {\r
-   if (tp==null || tree!=null)\r
-     return;\r
-   Vector leaves = new Vector();\r
-   tp.getTree().findLeaves(tp.getTree().getTopNode(), leaves);\r
-   Treenode[] tn = tree.getTreenode(); // todo: select nodes for this particular tree\r
-   int sz = tn.length;\r
-   int i = 0;\r
-   \r
-   while (i < sz)\r
-   {\r
-     Treenode node = tn[i++];\r
-     BinaryNode mappednode = findNodeSpec(node.getNodespec(),leaves);\r
-     if (mappednode!=null && mappednode instanceof SequenceNode) {\r
-       SequenceNode leaf = (SequenceNode) leaves.elementAt(i++);\r
-       // check if we can make the specified association\r
-       Object jvseq = null;\r
-       int vrf=0,refv=0;\r
-       while (jvseq==null && vrf<node.getVrefCount())\r
-       {\r
-         if (refv<node.getVref(vrf).getRefsCount())\r
-         {\r
-           Object noderef = node.getVref(vrf).getRefs(refv++);\r
-           if (noderef instanceof AlignmentSequence)\r
-           {\r
-             // we only make these kind of associations\r
-             jvseq = getvObj2jv((Vobject) noderef);\r
-           }\r
-         } else {\r
-           refv=0;\r
-           vrf++;\r
-         }\r
-       }\r
-       if (jvseq instanceof SequenceI)\r
-       {\r
-         leaf.setElement(jvseq);\r
-         leaf.setPlaceholder(false);\r
-       } else {\r
-         leaf.setPlaceholder(true);\r
-         leaf.setElement(new Sequence(leaf.getName(), "THISISAPLACEHLDER"));\r
-       }\r
-     }\r
-   }\r
- }\r
-\r
-  /// TODO: refactor to vamsas :start \r
-  /**\r
-   * construct treenode mappings for mapped sequences\r
-   * \r
-   * @param ntree\r
-   * @return\r
-   */\r
-  public Treenode[] makeTreeNodes(NJTree ntree) {\r
-    Vector leaves = new Vector();\r
-    ntree.findLeaves(ntree.getTopNode(), leaves);\r
-    Vector tnv = new Vector();\r
-    Enumeration l = leaves.elements();\r
-    Hashtable nodespecs = new Hashtable();\r
-    while (l.hasMoreElements())\r
-    {\r
-      jalview.datamodel.BinaryNode tnode = (jalview.datamodel.BinaryNode) l.nextElement();\r
-      if (tnode instanceof jalview.datamodel.SequenceNode)\r
-      {\r
-        if (!((jalview.datamodel.SequenceNode) tnode).isPlaceholder())\r
-        {\r
-          Object assocseq = ((jalview.datamodel.SequenceNode) tnode).element();\r
-          if (assocseq instanceof SequenceI)\r
-          {\r
-            Vobject vobj = this.getjv2vObj(assocseq);\r
-            if (vobj!=null)\r
-            {\r
-              Treenode node = new Treenode();\r
-              node.setNodespec(makeNodeSpec(nodespecs, tnode));\r
-              node.setName(tnode.getName());\r
-              Vref vr = new Vref();\r
-              vr.addRefs(vobj);\r
-              node.addVref(vr);\r
-              tnv.addElement(node);\r
-            }\r
-            else\r
-            {\r
-              System.err.println("WARNING: Unassociated treeNode "+tnode.element().toString()+" "\r
-                      +((tnode.getName()!=null) ? " label "+tnode.getName() : ""));\r
-            }\r
-          }\r
-        }\r
-      }\r
-    }\r
-    if (tnv.size()>0)\r
-    {\r
-      Treenode[] tn = new Treenode[tnv.size()];\r
-      tnv.copyInto(tn);  \r
-      return tn;\r
-    }\r
-    return new Treenode[] {};\r
-  }\r
-  private String makeNodeSpec(Hashtable nodespecs, jalview.datamodel.BinaryNode tnode)\r
-  { \r
-    String nname = new String(tnode.getName());\r
-    Integer nindx = (Integer) nodespecs.get(nname);\r
-    if (nindx==null)\r
-    {\r
-      nindx = new Integer(1);\r
-    }\r
-    nname = nindx.toString()+" "+nname;\r
-    return nname;\r
-  }\r
-  /**\r
-   * call to match up Treenode specs to NJTree parsed from document object.\r
-   * \r
-   * @param nodespec\r
-   * @param leaves\r
-   *          as returned from NJTree.findLeaves( .., ..) ..\r
-   * @return\r
-   */\r
-  private jalview.datamodel.BinaryNode findNodeSpec(String nodespec, Vector leaves)\r
-  {\r
-    int occurence=-1;\r
-    String nspec = nodespec.substring(nodespec.indexOf(' ')+1);\r
-    String oval = nodespec.substring(0, nodespec.indexOf(' '));\r
-    try {\r
-      occurence = new Integer(oval).intValue();\r
-    }\r
-    catch (Exception e)\r
-    {\r
-      System.err.println("Invalid nodespec '"+nodespec+"'");\r
-      return null;\r
-    }\r
-    jalview.datamodel.BinaryNode bn = null;\r
-    \r
-    int nocc = 0;\r
-    Enumeration en = leaves.elements();\r
-    while (en.hasMoreElements() && nocc<occurence)\r
-    {\r
-      bn = (jalview.datamodel.BinaryNode) en.nextElement();\r
-      if (bn instanceof jalview.datamodel.SequenceNode && bn.getName().equals(nspec))\r
-      {\r
-         --occurence;\r
-      } else \r
-        bn=null;\r
-    }\r
-    return bn;\r
-  }\r
-  // todo: end refactor to vamsas library\r
-  /**\r
-   * add jalview object to vamsas document\r
-   * \r
-   */\r
-  public void add() {\r
-    tree = new uk.ac.vamsas.objects.core.Tree();\r
-    bindjvvobj(tp, tree);\r
-    tree.setTitle(tp.getTitle());\r
-    Newick newick = new Newick();\r
-    newick.setContent(tp.getTree().toString());\r
-    newick.setTitle(tp.getTitle());\r
-    tree.addNewick(newick);\r
-    tree.setProvenance(makeTreeProvenance(jal, tp));\r
-    tree.setTreenode(makeTreeNodes(tp.getTree()));\r
-    \r
-    alignment.addTree(tree);\r
-  }\r
-\r
-  /**\r
-   * note: this function assumes that all sequence and alignment objects\r
-   * referenced in input data has already been associated with jalview objects.\r
-   * \r
-   * @param tp\r
-   * @return Object[] { AlignmentView, AlignmentI - reference alignment for\r
-   *         input }\r
-   */\r
-  public Object[] recoverInputData(Provenance tp)\r
-  {\r
-    AlignViewport javport=null;\r
-    jalview.datamodel.AlignmentI jal=null;\r
-    jalview.datamodel.CigarArray view=null;\r
-    for (int pe = 0; pe < tp.getEntryCount(); pe++)\r
-    {\r
-      if (tp.getEntry(pe).getInputCount() > 0)\r
-      {\r
-        if (tp.getEntry(pe).getInputCount() > 1)\r
-        {\r
-          Cache.log.warn("Ignoring additional input spec in provenance entry "\r
-                         + tp.getEntry(pe).toString());\r
-        }\r
-        // LATER: deal sensibly with multiple inputs\r
-        Input vInput = tp.getEntry(pe).getInput(0);\r
-        // is this the whole alignment or a specific set of sequences ?\r
-        if (vInput.getObjRefCount()==0)\r
-          continue;\r
-        if (vInput.getObjRefCount()==1 && vInput.getObjRef(0) instanceof uk.ac.vamsas.objects.core.Alignment)\r
-        {\r
-          // recover an AlignmentView for the input data\r
-          javport = (AlignViewport) getvObj2jv( (uk.ac.vamsas.\r
-              client.Vobject) vInput\r
-              .getObjRef(0));\r
-          jal = javport.getAlignment();\r
-          view = javport.getAlignment().\r
-              getCompactAlignment();\r
-        } else \r
-          if (vInput.getObjRef(0) instanceof uk.ac.vamsas.objects.core.AlignmentSequence) {\r
-            // recover an AlignmentView for the input data\r
-            javport = getViewport(((Vobject)vInput.getObjRef(0)).getV_parent());\r
-            jal = javport.getAlignment();\r
-            jalview.datamodel.SequenceI[] seqs = new jalview.datamodel.SequenceI[vInput.getObjRefCount()];\r
-            for (int i=0,iSize=vInput.getObjRefCount(); i<iSize; i++)\r
-            {\r
-              SequenceI seq = (SequenceI) getvObj2jv((Vobject) vInput.getObjRef(i));\r
-              seqs[i] = seq;\r
-            }\r
-            view = new jalview.datamodel.Alignment(seqs).getCompactAlignment();\r
-              \r
-            \r
-        }\r
-        int from = 1, to = jal.getWidth();\r
-        int offset = 0; // deleteRange modifies its frame of reference\r
-        for (int r = 0, s = vInput.getSegCount(); r < s; r++)\r
-        {\r
-          Seg visSeg = vInput.getSeg(r);\r
-          int se[] = getSegRange(visSeg, true); // jalview doesn't do\r
-                                                // bidirection alignments yet.\r
-          if (to < se[1])\r
-          {\r
-            Cache.log.warn("Ignoring invalid segment in InputData spec.");\r
-              }\r
-            else\r
-            {\r
-              if (se[0] > from)\r
-              {\r
-                view.deleteRange(offset + from - 1, offset + se[0] - 2);\r
-                offset -= se[0] - from;\r
-              }\r
-              from = se[1] + 1;\r
-            }\r
-          }\r
-          if (from < to)\r
-          {\r
-            view.deleteRange(offset + from - 1, offset + to - 1); // final\r
-                                                                  // deletion -\r
-                                                                  // TODO: check\r
-                                                                  // off by\r
-            // one for to\r
-          }\r
-          return new Object[]{new AlignmentView(view), jal};\r
-        }\r
-    }\r
-    Cache.log.debug("Returning null for input data recovery from provenance.");\r
-    return null;\r
-  }\r
-  \r
-  private AlignViewport getViewport(Vobject v_parent)\r
-  {\r
-    if (v_parent instanceof uk.ac.vamsas.objects.core.Alignment)\r
-    {\r
-      return datastore.findViewport((uk.ac.vamsas.objects.core.Alignment) v_parent);\r
-    }\r
-    return null;\r
-  }\r
-  \r
-  public NewickFile getNewickTree()\r
-  {\r
-    return ntree;\r
-  }\r
-  public String getTitle()\r
-  {\r
-    return title;\r
-  }\r
-  public AlignmentView getInputData()\r
-  {\r
-    return inputData;\r
-  }\r
-  public boolean isValidTree()\r
-  {\r
-    try {\r
-      ntree.parse();\r
-      if (ntree.getTree()!=null)\r
-      {\r
-        ntree = getNtree();\r
-      }\r
-        return true;\r
-    }\r
-    catch (Exception e)\r
-    {\r
-      Cache.log.debug("Failed to parse newick tree string",e);\r
-    }\r
-    return false;\r
-  }\r
-}\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview 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 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview 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 Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.vamsas;
+
+import jalview.analysis.NJTree;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.BinaryNode;
+import jalview.datamodel.SeqCigar;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.gui.TreePanel;
+import jalview.io.NewickFile;
+import jalview.io.VamsasAppDatastore;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import uk.ac.vamsas.client.Vobject;
+import uk.ac.vamsas.objects.core.AlignmentSequence;
+import uk.ac.vamsas.objects.core.Entry;
+import uk.ac.vamsas.objects.core.Input;
+import uk.ac.vamsas.objects.core.Newick;
+import uk.ac.vamsas.objects.core.Param;
+import uk.ac.vamsas.objects.core.Provenance;
+import uk.ac.vamsas.objects.core.Seg;
+import uk.ac.vamsas.objects.core.Treenode;
+import uk.ac.vamsas.objects.core.Vref;
+
+public class Tree extends DatastoreItem
+{
+  AlignmentI jal;
+
+  TreePanel tp;
+
+  uk.ac.vamsas.objects.core.Tree tree;
+
+  uk.ac.vamsas.objects.core.Alignment alignment; // may be null => dataset or
+
+  // other kind of tree
+  private NewickFile ntree;
+
+  private String title;
+
+  private AlignmentView inputData = null;
+
+  public static void updateFrom(VamsasAppDatastore datastore,
+          jalview.gui.AlignFrame alignFrame,
+          uk.ac.vamsas.objects.core.Tree vtree)
+  {
+    Tree toTree = new Tree(datastore, alignFrame, vtree);
+  }
+
+  public Tree(VamsasAppDatastore datastore,
+          jalview.gui.AlignFrame alignFrame,
+          uk.ac.vamsas.objects.core.Tree vtree)
+  {
+    super(datastore, vtree, TreePanel.class);
+    doJvUpdate();
+  }
+
+  private NewickFile getNtree() throws IOException
+  {
+    return new jalview.io.NewickFile(tree.getNewick(0).getContent());
+  }
+
+  public Tree(VamsasAppDatastore datastore, TreePanel tp2, AlignmentI jal2,
+          uk.ac.vamsas.objects.core.Alignment alignment2)
+  {
+    super(datastore, tp2, uk.ac.vamsas.objects.core.Tree.class);
+
+    jal = jal2;
+    tp = (TreePanel) jvobj;
+    alignment = alignment2;
+
+    tree = (uk.ac.vamsas.objects.core.Tree) vobj;
+    doSync();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.vamsas.DatastoreItem#addFromDocument()
+   */
+  @Override
+  public void addFromDocument()
+  {
+    tree = (uk.ac.vamsas.objects.core.Tree) vobj; // vtree;
+    TreePanel tp = (TreePanel) jvobj; // getvObj2jv(tree);
+    // make a new tree
+    Object[] idata = recoverInputData(tree.getProvenance());
+    try
+    {
+      if (idata != null && idata[0] != null)
+      {
+        inputData = (AlignmentView) idata[0];
+      }
+      ntree = getNtree();
+      title = tree.getNewick(0).getTitle();
+      if (title == null || title.length() == 0)
+      {
+        title = tree.getTitle(); // hack!!!!
+      }
+    } catch (Exception e)
+    {
+      Cache.log.warn("Problems parsing treefile '"
+              + tree.getNewick(0).getContent() + "'", e);
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.vamsas.DatastoreItem#conflict()
+   */
+  @Override
+  public void conflict()
+  {
+    Cache.log
+            .info("Update (with conflict) from vamsas document to alignment associated tree not implemented yet.");
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.vamsas.DatastoreItem#update()
+   */
+  @Override
+  public void updateToDoc()
+  {
+    if (isModifiable(tree.getModifiable()))
+    {
+      // synchronize(); // update();
+      // verify any changes.
+      log.info("TODO: Update tree in document from jalview.");
+    }
+    else
+    {
+      // handle conflict
+      log.info("TODO: Add the locally modified tree in Jalview as a new tree in document, leaving locked tree unchanged.");
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.vamsas.DatastoreItem#updateFromDoc()
+   */
+  @Override
+  public void updateFromDoc()
+  {
+    // should probably just open a new tree panel in the same place as the old
+    // one
+    // TODO: Tree.updateFromDoc
+    /*
+     * TreePanel tp = (TreePanel) jvobj; // getvObj2jv(tree);
+     * 
+     * // make a new tree Object[] idata =
+     * recoverInputData(tree.getProvenance()); try { if (idata != null &&
+     * idata[0] != null) { inputData = (AlignmentView) idata[0]; } ntree =
+     * getNtree(); title = tree.getNewick(0).getTitle(); if (title == null ||
+     * title.length() == 0) { title = tree.getTitle(); // hack!!!! } } catch
+     * (Exception e) { Cache.log.warn("Problems parsing treefile '" +
+     * tree.getNewick(0).getContent() + "'", e); }
+     */
+    log.debug("Update the local tree in jalview from the document.");
+
+    if (isModifiable(tree.getModifiable()))
+    {
+      // synchronize(); // update();
+      // verify any changes.
+      log.debug("Update tree in document from jalview.");
+    }
+    else
+    {
+      // handle conflict
+      log.debug("Add modified jalview tree as new tree in document.");
+    }
+  }
+
+  /**
+   * correctly creates provenance for trees calculated on an alignment by
+   * jalview.
+   * 
+   * @param jal
+   * @param tp
+   * @return
+   */
+  private Provenance makeTreeProvenance(AlignmentI jal, TreePanel tp)
+  {
+    Cache.log.debug("Making Tree provenance for " + tp.getTitle());
+    Provenance prov = new Provenance();
+    prov.addEntry(new Entry());
+    prov.getEntry(0).setAction("imported " + tp.getTitle());
+    prov.getEntry(0).setUser(provEntry.getUser());
+    prov.getEntry(0).setApp(provEntry.getApp());
+    prov.getEntry(0).setDate(provEntry.getDate());
+    if (tp.getTree().hasOriginalSequenceData())
+    {
+      Input vInput = new Input();
+      // LATER: check to see if tree input data is contained in this alignment -
+      // or just correctly resolve the tree's seqData to the correct alignment
+      // in
+      // the document.
+      Vector alsqrefs = getjv2vObjs(findAlignmentSequences(jal,
+              tp.getTree().seqData.getSequences()));
+      Object[] alsqs = new Object[alsqrefs.size()];
+      alsqrefs.copyInto(alsqs);
+      vInput.setObjRef(alsqs);
+      // now create main provenance data
+      prov.getEntry(0).setAction("created " + tp.getTitle());
+      prov.getEntry(0).addInput(vInput);
+      // jalview's special input parameter for distance matrix calculations
+      vInput.setName("jalview:seqdist"); // TODO: settle on appropriate name.
+      prov.getEntry(0).addParam(new Param());
+      prov.getEntry(0).getParam(0).setName("treeType");
+      prov.getEntry(0).getParam(0).setType("utf8");
+      prov.getEntry(0).getParam(0).setContent("NJ"); // TODO: type of tree is a
+      // general parameter
+      int ranges[] = tp.getTree().seqData.getVisibleContigs();
+      // VisibleContigs are with respect to alignment coordinates. Still need
+      // offsets
+      int start = tp.getTree().seqData.getAlignmentOrigin();
+      for (int r = 0; r < ranges.length; r += 2)
+      {
+        Seg visSeg = new Seg();
+        visSeg.setStart(1 + start + ranges[r]);
+        visSeg.setEnd(start + ranges[r + 1]);
+        visSeg.setInclusive(true);
+        vInput.addSeg(visSeg);
+      }
+    }
+    Cache.log.debug("Finished Tree provenance for " + tp.getTitle());
+    return prov;
+  }
+
+  /**
+   * look up SeqCigars in an existing alignment.
+   * 
+   * @param jal
+   * @param sequences
+   * @return vector of alignment sequences in order of SeqCigar array (but
+   *         missing unfound seqcigars)
+   */
+  private Vector findAlignmentSequences(AlignmentI jal, SeqCigar[] sequences)
+  {
+    SeqCigar[] tseqs = new SeqCigar[sequences.length];
+    System.arraycopy(sequences, 0, tseqs, 0, sequences.length);
+    Vector alsq = new Vector();
+    List<SequenceI> jalsqs;
+    synchronized (jalsqs = jal.getSequences())
+    {
+      for (SequenceI asq : jalsqs)
+      {
+        for (int t = 0; t < sequences.length; t++)
+        {
+          if (tseqs[t] != null
+                  && (tseqs[t].getRefSeq() == asq || tseqs[t].getRefSeq() == asq
+                          .getDatasetSequence()))
+          // && tseqs[t].getStart()>=asq.getStart() &&
+          // tseqs[t].getEnd()<=asq.getEnd())
+          {
+            tseqs[t] = null;
+            alsq.add(asq);
+          }
+        }
+      }
+    }
+    if (alsq.size() < sequences.length)
+    {
+      Cache.log
+              .warn("Not recovered all alignment sequences for given set of input sequence CIGARS");
+    }
+    return alsq;
+  }
+
+  /**
+   * 
+   * Update jalview newick representation with TreeNode map
+   * 
+   * @param tp
+   *          the treepanel that this tree is bound to.
+   */
+  public void UpdateSequenceTreeMap(TreePanel tp)
+  {
+    if (tp == null || tree == null)
+    {
+      return;
+    }
+
+    if (tp.getTree() == null)
+    {
+      Cache.log.warn("Not updating SequenceTreeMap for "
+              + tree.getVorbaId());
+      return;
+    }
+    Vector<SequenceNode> leaves = tp.getTree().findLeaves(
+            tp.getTree().getTopNode());
+    Treenode[] tn = tree.getTreenode(); // todo: select nodes for this
+    // particular tree
+    int sz = tn.length;
+    int i = 0;
+
+    while (i < sz)
+    {
+      Treenode node = tn[i++];
+      BinaryNode mappednode = findNodeSpec(node.getNodespec(), leaves);
+      if (mappednode != null && mappednode instanceof SequenceNode)
+      {
+        SequenceNode leaf = (SequenceNode) mappednode;
+        // check if we can make the specified association
+        Object jvseq = null;
+        int vrf = 0, refv = 0;
+        while (jvseq == null && vrf < node.getVrefCount())
+        {
+          if (refv < node.getVref(vrf).getRefsCount())
+          {
+            Object noderef = node.getVref(vrf).getRefs(refv++);
+            if (noderef instanceof AlignmentSequence)
+            {
+              // we only make these kind of associations
+              jvseq = getvObj2jv((Vobject) noderef);
+            }
+          }
+          else
+          {
+            refv = 0;
+            vrf++;
+          }
+        }
+        if (jvseq instanceof SequenceI)
+        {
+          leaf.setElement(jvseq);
+          leaf.setPlaceholder(false);
+        }
+        else
+        {
+          leaf.setPlaceholder(true);
+          leaf.setElement(new Sequence(leaf.getName(), "THISISAPLACEHLDER"));
+        }
+      }
+    }
+  }
+
+  // / TODO: refactor to vamsas :start
+  /**
+   * construct treenode mappings for mapped sequences
+   * 
+   * @param ntree
+   * @param newick
+   * @return
+   */
+  public Treenode[] makeTreeNodes(NJTree ntree, Newick newick)
+  {
+    Vector<SequenceNode> leaves = ntree.findLeaves(ntree.getTopNode());
+    Vector tnv = new Vector();
+    Enumeration l = leaves.elements();
+    Hashtable nodespecs = new Hashtable();
+    while (l.hasMoreElements())
+    {
+      jalview.datamodel.BinaryNode tnode = (jalview.datamodel.BinaryNode) l
+              .nextElement();
+      if (tnode instanceof jalview.datamodel.SequenceNode)
+      {
+        if (!((jalview.datamodel.SequenceNode) tnode).isPlaceholder())
+        {
+          Object assocseq = ((jalview.datamodel.SequenceNode) tnode)
+                  .element();
+          if (assocseq instanceof SequenceI)
+          {
+            Vobject vobj = this.getjv2vObj(assocseq);
+            if (vobj != null)
+            {
+              Treenode node = new Treenode();
+              if (newick.isRegisterable())
+              {
+                this.cdoc.registerObject(newick);
+                node.addTreeId(newick);
+              }
+              node.setNodespec(makeNodeSpec(nodespecs, tnode));
+              node.setName(tnode.getName());
+              Vref vr = new Vref();
+              vr.addRefs(vobj);
+              node.addVref(vr);
+              tnv.addElement(node);
+            }
+            else
+            {
+              System.err.println("WARNING: Unassociated treeNode "
+                      + tnode.element().toString()
+                      + " "
+                      + ((tnode.getName() != null) ? " label "
+                              + tnode.getName() : ""));
+            }
+          }
+        }
+      }
+    }
+    if (tnv.size() > 0)
+    {
+      Treenode[] tn = new Treenode[tnv.size()];
+      tnv.copyInto(tn);
+      return tn;
+    }
+    return new Treenode[] {};
+  }
+
+  private String makeNodeSpec(Hashtable nodespecs,
+          jalview.datamodel.BinaryNode tnode)
+  {
+    String nname = new String(tnode.getName());
+    Integer nindx = (Integer) nodespecs.get(nname);
+    if (nindx == null)
+    {
+      nindx = new Integer(1);
+    }
+    nname = nindx.toString() + " " + nname;
+    return nname;
+  }
+
+  /**
+   * call to match up Treenode specs to NJTree parsed from document object.
+   * 
+   * @param nodespec
+   * @param leaves
+   *          as returned from NJTree.findLeaves( .., ..) ..
+   * @return
+   */
+  private jalview.datamodel.BinaryNode findNodeSpec(String nodespec,
+          Vector leaves)
+  {
+    int occurence = -1;
+    String nspec = nodespec.substring(nodespec.indexOf(' ') + 1);
+    String oval = nodespec.substring(0, nodespec.indexOf(' '));
+    try
+    {
+      occurence = new Integer(oval).intValue();
+    } catch (Exception e)
+    {
+      System.err.println("Invalid nodespec '" + nodespec + "'");
+      return null;
+    }
+    jalview.datamodel.BinaryNode bn = null;
+
+    int nocc = 0;
+    Enumeration en = leaves.elements();
+    while (en.hasMoreElements() && nocc < occurence)
+    {
+      bn = (jalview.datamodel.BinaryNode) en.nextElement();
+      if (bn instanceof jalview.datamodel.SequenceNode
+              && bn.getName().equals(nspec))
+      {
+        --occurence;
+      }
+      else
+      {
+        bn = null;
+      }
+    }
+    return bn;
+  }
+
+  // todo: end refactor to vamsas library
+  /**
+   * add jalview object to vamsas document
+   * 
+   */
+  @Override
+  public void addToDocument()
+  {
+    tree = new uk.ac.vamsas.objects.core.Tree();
+    bindjvvobj(tp, tree);
+    tree.setTitle(tp.getTitle());
+    Newick newick = new Newick();
+    newick.setContent(tp.getTree().toString());
+    newick.setTitle(tp.getTitle());
+    tree.addNewick(newick);
+    tree.setProvenance(makeTreeProvenance(jal, tp));
+    tree.setTreenode(makeTreeNodes(tp.getTree(), newick));
+
+    alignment.addTree(tree);
+  }
+
+  /**
+   * note: this function assumes that all sequence and alignment objects
+   * referenced in input data has already been associated with jalview objects.
+   * 
+   * @param tp
+   * @param alignFrame
+   * @return Object[] { AlignmentView, AlignmentI - reference alignment for
+   *         input }
+   */
+  public Object[] recoverInputData(Provenance tp)
+  {
+    AlignmentViewport javport = null;
+    jalview.datamodel.AlignmentI jal = null;
+    jalview.datamodel.CigarArray view = null;
+    for (int pe = 0; pe < tp.getEntryCount(); pe++)
+    {
+      if (tp.getEntry(pe).getInputCount() > 0)
+      {
+        if (tp.getEntry(pe).getInputCount() > 1)
+        {
+          Cache.log
+                  .warn("Ignoring additional input spec in provenance entry "
+                          + tp.getEntry(pe).toString());
+        }
+        // LATER: deal sensibly with multiple inputs
+        Input vInput = tp.getEntry(pe).getInput(0);
+        // is this the whole alignment or a specific set of sequences ?
+        if (vInput.getObjRefCount() == 0)
+        {
+          if (tree.getV_parent() != null
+                  && tree.getV_parent() instanceof uk.ac.vamsas.objects.core.Alignment)
+          {
+            javport = getViewport(tree.getV_parent());
+            jal = javport.getAlignment();
+            view = javport.getAlignment().getCompactAlignment();
+          }
+        }
+        else
+        {
+          // Explicit reference - to alignment, sequences or what.
+          if (vInput.getObjRefCount() == 1
+                  && vInput.getObjRef(0) instanceof uk.ac.vamsas.objects.core.Alignment)
+          {
+            // recover an AlignmentView for the input data
+            javport = getViewport((Vobject) vInput.getObjRef(0));
+            jal = javport.getAlignment();
+            view = javport.getAlignment().getCompactAlignment();
+          }
+          else if (vInput.getObjRef(0) instanceof uk.ac.vamsas.objects.core.AlignmentSequence)
+          {
+            // recover an AlignmentView for the input data
+            javport = getViewport(((Vobject) vInput.getObjRef(0))
+                    .getV_parent());
+            jal = javport.getAlignment();
+            jalview.datamodel.SequenceI[] seqs = new jalview.datamodel.SequenceI[vInput
+                    .getObjRefCount()];
+            for (int i = 0, iSize = vInput.getObjRefCount(); i < iSize; i++)
+            {
+              SequenceI seq = (SequenceI) getvObj2jv((Vobject) vInput
+                      .getObjRef(i));
+              seqs[i] = seq;
+            }
+            view = new jalview.datamodel.Alignment(seqs)
+                    .getCompactAlignment();
+
+          }
+        }
+        int from = 1, to = jal.getWidth();
+        int offset = 0; // deleteRange modifies its frame of reference
+        for (int r = 0, s = vInput.getSegCount(); r < s; r++)
+        {
+          Seg visSeg = vInput.getSeg(r);
+          int se[] = getSegRange(visSeg, true); // jalview doesn't do
+          // bidirection alignments yet.
+          if (to < se[1])
+          {
+            Cache.log.warn("Ignoring invalid segment in InputData spec.");
+          }
+          else
+          {
+            if (se[0] > from)
+            {
+              view.deleteRange(offset + from - 1, offset + se[0] - 2);
+              offset -= se[0] - from;
+            }
+            from = se[1] + 1;
+          }
+        }
+        if (from < to)
+        {
+          view.deleteRange(offset + from - 1, offset + to - 1); // final
+          // deletion -
+          // TODO: check
+          // off by
+          // one for to
+        }
+        return new Object[] { new AlignmentView(view), jal };
+      }
+    }
+    Cache.log
+            .debug("Returning null for input data recovery from provenance.");
+    return null;
+  }
+
+  private AlignmentViewport getViewport(Vobject v_parent)
+  {
+    if (v_parent instanceof uk.ac.vamsas.objects.core.Alignment)
+    {
+      return datastore
+              .findViewport((uk.ac.vamsas.objects.core.Alignment) v_parent);
+    }
+    return null;
+  }
+
+  public NewickFile getNewickTree()
+  {
+    return ntree;
+  }
+
+  public String getTitle()
+  {
+    return title;
+  }
+
+  public AlignmentView getInputData()
+  {
+    return inputData;
+  }
+
+  public boolean isValidTree()
+  {
+    try
+    {
+      if (ntree == null)
+      {
+        return false;
+      }
+      ntree.parse();
+      if (ntree.getTree() != null)
+      {
+        ntree = getNtree();
+      }
+      return true;
+    } catch (Exception e)
+    {
+      Cache.log.debug("Failed to parse newick tree string", e);
+    }
+    return false;
+  }
+}