--- /dev/null
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\r
+ */\r
+\r
+// NewickFile.java\r
+// Tree I/O\r
+// http://evolution.genetics.washington.edu/phylip/newick_doc.html\r
+// TODO: Implement Basic NHX tag parsing and preservation\r
+// TODO: http://evolution.genetics.wustl.edu/eddy/forester/NHX.html\r
+// TODO: Extended SequenceNodeI to hold parsed NHX strings\r
+package uk.ac.vamsas.objects.utils;\r
+\r
+import java.io.*;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import uk.ac.vamsas.client.Vobject;\r
+import uk.ac.vamsas.client.VorbaId;\r
+import uk.ac.vamsas.objects.core.SequenceType;\r
+\r
+/**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @author $author$\r
+ * @version $Revision: 1.12 $\r
+ */\r
+public class NewickFile {\r
+ public class BinaryNode {\r
+ VorbaId element;\r
+\r
+ String name;\r
+\r
+ BinaryNode left;\r
+\r
+ BinaryNode right;\r
+\r
+ BinaryNode parent;\r
+\r
+ /** DOCUMENT ME!! */\r
+ public int bootstrap;\r
+\r
+ /**\r
+ * Creates a new BinaryNode object.\r
+ */\r
+ public BinaryNode() {\r
+ left = right = parent = null;\r
+ bootstrap = 0;\r
+ }\r
+\r
+ /**\r
+ * Creates a new BinaryNode object.\r
+ * \r
+ * @param element\r
+ * DOCUMENT ME!\r
+ * @param parent\r
+ * DOCUMENT ME!\r
+ * @param name\r
+ * DOCUMENT ME!\r
+ */\r
+ public BinaryNode(VorbaId element, BinaryNode parent, String name) {\r
+ this.element = element;\r
+ this.parent = parent;\r
+ this.name = name;\r
+\r
+ left = right = null;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public VorbaId element() {\r
+ return element;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param v\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public VorbaId setElement(VorbaId v) {\r
+ return element = v;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode left() {\r
+ return left;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param n\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode setLeft(BinaryNode n) {\r
+ return left = n;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode right() {\r
+ return right;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param n\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode setRight(BinaryNode n) {\r
+ return right = n;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode parent() {\r
+ return parent;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param n\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public BinaryNode setParent(BinaryNode n) {\r
+ return parent = n;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public boolean isLeaf() {\r
+ return (left == null) && (right == null);\r
+ }\r
+\r
+ /**\r
+ * attaches FIRST and SECOND node arguments as the LEFT and RIGHT children\r
+ * of this node (removing any old references) a null parameter DOES NOT mean\r
+ * that the pointer to the corresponding child node is set to NULL - you\r
+ * should use setChild(null), or detach() for this.\r
+ * \r
+ */\r
+ public void SetChildren(BinaryNode leftchild, BinaryNode rightchild) {\r
+ if (leftchild != null) {\r
+ this.setLeft(leftchild);\r
+ leftchild.detach();\r
+ leftchild.setParent(this);\r
+ }\r
+\r
+ if (rightchild != null) {\r
+ this.setRight(rightchild);\r
+ rightchild.detach();\r
+ rightchild.setParent(this);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Detaches the node from the binary tree, along with all its child nodes.\r
+ * \r
+ * @return BinaryNode The detached node.\r
+ */\r
+ public BinaryNode detach() {\r
+ if (this.parent != null) {\r
+ if (this.parent.left == this) {\r
+ this.parent.left = null;\r
+ } else {\r
+ if (this.parent.right == this) {\r
+ this.parent.right = null;\r
+ }\r
+ }\r
+ }\r
+\r
+ this.parent = null;\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Traverses up through the tree until a node with a free leftchild is\r
+ * discovered.\r
+ * \r
+ * @return BinaryNode\r
+ */\r
+ public BinaryNode ascendLeft() {\r
+ BinaryNode c = this;\r
+\r
+ do {\r
+ c = c.parent();\r
+ } while ((c != null) && (c.left() != null) && !c.left().isLeaf());\r
+\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Traverses up through the tree until a node with a free rightchild is\r
+ * discovered. Jalview builds trees by descent on the left, so this may be\r
+ * unused.\r
+ * \r
+ * @return BinaryNode\r
+ */\r
+ public BinaryNode ascendRight() {\r
+ BinaryNode c = this;\r
+\r
+ do {\r
+ c = c.parent();\r
+ } while ((c != null) && (c.right() != null) && !c.right().isLeaf());\r
+\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param name\r
+ * DOCUMENT ME!\r
+ */\r
+ public void setName(String name) {\r
+ this.name = name;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public String getName() {\r
+ return this.name;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param boot\r
+ * DOCUMENT ME!\r
+ */\r
+ public void setBootstrap(int boot) {\r
+ this.bootstrap = boot;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public int getBootstrap() {\r
+ return bootstrap;\r
+ }\r
+ /**\r
+ * \r
+ * @return unquoted string of form VorbaId|v|node name string\r
+ */\r
+ public String getNewickNodeName() {\r
+ if (element!=null || name!=null)\r
+ {\r
+ return ((element!=null) ? element.getId()+"|v|" : "")\r
+ + ((name == null) ? "" : name);\r
+ }\r
+ return "";\r
+ }\r
+ public boolean parseNodeNameString(uk.ac.vamsas.client.ClientDocument binder)\r
+ {\r
+ \r
+ if (element==null && name!=null && name.indexOf("|v|")>-1)\r
+ {\r
+ element = binder.getObject(null).getVorbaId(); // TODO: fix THIS ! name.substring(0, name.indexOf("|v")));\r
+ \r
+ }\r
+ return false;\r
+ }\r
+ }\r
+\r
+ public class SequenceNode extends BinaryNode {\r
+ /** DOCUMENT ME!! */\r
+ public float dist;\r
+\r
+ /** DOCUMENT ME!! */\r
+ public boolean dummy = false;\r
+\r
+ private boolean placeholder = false;\r
+\r
+ /**\r
+ * Creates a new SequenceNode object.\r
+ */\r
+ public SequenceNode() {\r
+ super();\r
+ }\r
+\r
+ /**\r
+ * Creates a new SequenceNode object.\r
+ * \r
+ * @param val\r
+ * DOCUMENT ME!\r
+ * @param parent\r
+ * DOCUMENT ME!\r
+ * @param dist\r
+ * DOCUMENT ME!\r
+ * @param name\r
+ * DOCUMENT ME!\r
+ */\r
+ public SequenceNode(VorbaId val, SequenceNode parent, float dist, String name) {\r
+ super(val, parent, name);\r
+ this.dist = dist;\r
+ }\r
+ public SequenceNode(Vobject val, SequenceNode parent, float dist, String name) {\r
+ super(val.getVorbaId(), parent, name);\r
+ this.dist = dist;\r
+ }\r
+\r
+ /**\r
+ * Creates a new SequenceNode object.\r
+ * \r
+ * @param val\r
+ * DOCUMENT ME!\r
+ * @param parent\r
+ * DOCUMENT ME!\r
+ * @param name\r
+ * DOCUMENT ME!\r
+ * @param dist\r
+ * DOCUMENT ME!\r
+ * @param bootstrap\r
+ * DOCUMENT ME!\r
+ * @param dummy\r
+ * DOCUMENT ME!\r
+ */\r
+ public SequenceNode(Vobject val, SequenceNode parent, String name,\r
+ float dist, int bootstrap, boolean dummy) {\r
+ super(val.getVorbaId(), parent, name);\r
+ this.dist = dist;\r
+ this.bootstrap = bootstrap;\r
+ this.dummy = dummy;\r
+ }\r
+\r
+ /**\r
+ * @param dummy\r
+ * true if node is created for the representation of polytomous\r
+ * trees\r
+ */\r
+ public boolean isDummy() {\r
+ return dummy;\r
+ }\r
+\r
+ /*\r
+ * @param placeholder is true if the sequence refered to in the element node\r
+ * is not actually present in the associated alignment\r
+ */\r
+ public boolean isPlaceholder() {\r
+ return placeholder;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param newstate\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public boolean setDummy(boolean newstate) {\r
+ boolean oldstate = dummy;\r
+ dummy = newstate;\r
+\r
+ return oldstate;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param Placeholder\r
+ * DOCUMENT ME!\r
+ */\r
+ public void setPlaceholder(boolean Placeholder) {\r
+ this.placeholder = Placeholder;\r
+ }\r
+\r
+ /**\r
+ * ascends the tree but doesn't stop until a non-dummy node is discovered.\r
+ * This will probably break if the tree is a mixture of BinaryNodes and\r
+ * SequenceNodes.\r
+ */\r
+ public SequenceNode AscendTree() {\r
+ SequenceNode c = this;\r
+\r
+ do {\r
+ c = (SequenceNode) c.parent();\r
+ } while ((c != null) && c.dummy);\r
+\r
+ return c;\r
+ }\r
+\r
+ }\r
+\r
+ SequenceNode root;\r
+\r
+ private boolean HasBootstrap = false;\r
+\r
+ private boolean HasDistances = false;\r
+\r
+ private boolean RootHasDistance = false;\r
+\r
+ // File IO Flags\r
+ boolean ReplaceUnderscores = false;\r
+\r
+ boolean printRootInfo = false;\r
+\r
+ private Pattern[] NodeSafeName = new Pattern[] {\r
+ Pattern.compile("[\\[,:'()]"), // test for requiring quotes\r
+ Pattern.compile("'"), // escaping quote characters\r
+ Pattern.compile("/w") // unqoted whitespace transformation\r
+ };\r
+\r
+ char QuoteChar = '\'';\r
+\r
+ String newickFile = null;\r
+\r
+ /**\r
+ * Creates a new NewickFile object.\r
+ * \r
+ * @param inStr\r
+ * DOCUMENT ME!\r
+ * \r
+ * @throws IOException\r
+ * DOCUMENT ME!\r
+ */\r
+ public NewickFile(String inStr) throws IOException {\r
+ newickFile = inStr;\r
+ parse();\r
+ }\r
+ public NewickFile(File inFile) throws IOException {\r
+ errormessage = "Problem's reading file "+inFile;\r
+ dataIn = new java.io.BufferedReader(new InputStreamReader(new java.io.FileInputStream(inFile)));\r
+ parse();\r
+ }\r
+ /**\r
+ * Creates a new NewickFile object.\r
+ * \r
+ * @param newtree\r
+ * DOCUMENT ME!\r
+ */\r
+ public NewickFile(SequenceNode newtree) {\r
+ root = newtree;\r
+ }\r
+\r
+ /**\r
+ * Creates a new NewickFile object.\r
+ * \r
+ * @param newtree\r
+ * DOCUMENT ME!\r
+ * @param bootstrap\r
+ * DOCUMENT ME!\r
+ */\r
+ public NewickFile(SequenceNode newtree, boolean bootstrap) {\r
+ HasBootstrap = bootstrap;\r
+ root = newtree;\r
+ }\r
+\r
+ /**\r
+ * Creates a new NewickFile object.\r
+ * \r
+ * @param newtree\r
+ * DOCUMENT ME!\r
+ * @param bootstrap\r
+ * DOCUMENT ME!\r
+ * @param distances\r
+ * DOCUMENT ME!\r
+ */\r
+ public NewickFile(SequenceNode newtree, boolean bootstrap, boolean distances) {\r
+ root = newtree;\r
+ HasBootstrap = bootstrap;\r
+ HasDistances = distances;\r
+ }\r
+\r
+ /**\r
+ * Creates a new NewickFile object.\r
+ * \r
+ * @param newtree\r
+ * DOCUMENT ME!\r
+ * @param bootstrap\r
+ * DOCUMENT ME!\r
+ * @param distances\r
+ * DOCUMENT ME!\r
+ * @param rootdistance\r
+ * DOCUMENT ME!\r
+ */\r
+ public NewickFile(SequenceNode newtree, boolean bootstrap, boolean distances,\r
+ boolean rootdistance) {\r
+ root = newtree;\r
+ HasBootstrap = bootstrap;\r
+ HasDistances = distances;\r
+ RootHasDistance = rootdistance;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param Error\r
+ * Message prefix\r
+ * @param Er\r
+ * Message qualifier (ie the incorrect data)\r
+ * @param r\r
+ * range of characters either side to dump\r
+ * @param p\r
+ * character position\r
+ * @param s\r
+ * newick string being parsed\r
+ * \r
+ * @return composed error message\r
+ */\r
+ private String ErrorStringrange(String Error, String Er, int r, int p,\r
+ String s) {\r
+ return ((Error == null) ? "" : Error)\r
+ + Er\r
+ + " at position "\r
+ + p\r
+ + " ( "\r
+ + s.substring(((p - r) < 0) ? 0 : (p - r), ((p + r) > s.length()) ? s\r
+ .length() : (p + r)) + " )\n";\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @return true if tree has bootstrap values\r
+ */\r
+ public boolean HasBootstrap() {\r
+ return HasBootstrap;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @return true if tree has distances on branches\r
+ */\r
+ public boolean HasDistances() {\r
+ return HasDistances;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @return true if root has a stem distance\r
+ */\r
+ public boolean HasRootDistance() {\r
+ return RootHasDistance;\r
+ }\r
+ /*\r
+ * hacked out of jalview code\r
+ */\r
+ boolean error;\r
+ String errormessage;\r
+ java.io.BufferedReader dataIn=null;\r
+ public String nextLine() throws IOException {\r
+ if (dataIn==null)\r
+ {\r
+ dataIn = new BufferedReader(new StringReader(newickFile));\r
+ error=false;\r
+ }\r
+ else\r
+ throw new IOException("IMPLEMENTATION ERROR: NewickFile has not been initialised for reading a newick string.");\r
+ if (!error)\r
+ return dataIn.readLine();\r
+ throw new IOException("Invalid Source Stream:" + errormessage);\r
+ }\r
+ /**\r
+ * call this to convert the newick string into a binary node linked tree\r
+ * \r
+ * @throws IOException\r
+ * if the newick string cannot be parsed.\r
+ */\r
+ public void parse() throws IOException {\r
+ String nf;\r
+ if (newickFile==null)\r
+ {\r
+ // fill nf with complete tree file\r
+\r
+ StringBuffer file = new StringBuffer();\r
+\r
+ while ((nf = nextLine()) != null) {\r
+ file.append(nf);\r
+ }\r
+\r
+ nf = file.toString();\r
+ } else\r
+ {\r
+ nf = newickFile;\r
+ }\r
+ \r
+\r
+ root = new SequenceNode();\r
+\r
+ SequenceNode realroot = null;\r
+ SequenceNode c = root;\r
+\r
+ int d = -1;\r
+ int cp = 0;\r
+ // int flen = nf.length();\r
+\r
+ String Error = null;\r
+ String nodename = null;\r
+\r
+ float DefDistance = (float) 0.001; // @param Default distance for a node -\r
+ // very very small\r
+ int DefBootstrap = 0; // @param Default bootstrap for a node\r
+\r
+ float distance = DefDistance;\r
+ int bootstrap = DefBootstrap;\r
+\r
+ boolean ascending = false; // flag indicating that we are leaving the\r
+ // current node\r
+\r
+ Pattern majorsyms = Pattern.compile(\r
+ "[(\\['),;]");\r
+\r
+ Matcher mjsyms = majorsyms.matcher(nf);\r
+ while (mjsyms.find(cp) && (Error == null)) {\r
+ int fcp = mjsyms.start();\r
+\r
+ switch (nf.charAt(fcp)) {\r
+ case '[': // Comment or structured/extended NH format info\r
+\r
+\r
+ if (nf.indexOf(']',fcp)>-1) {\r
+ // Skip the comment field\r
+ cp = nf.indexOf(']',fcp);\r
+ } else {\r
+ Error = ErrorStringrange(Error, "Unterminated comment", 3, fcp, nf);\r
+ }\r
+\r
+ ;\r
+\r
+ break;\r
+\r
+ case '(':\r
+\r
+ // ascending should not be set\r
+ // New Internal node\r
+ if (ascending) {\r
+ Error = ErrorStringrange(Error, "Unexpected '('", 7, fcp, nf);\r
+\r
+ continue;\r
+ }\r
+\r
+ ;\r
+ d++;\r
+\r
+ if (c.right() == null) {\r
+ c.setRight(new SequenceNode(null, c, null, DefDistance, DefBootstrap,\r
+ false));\r
+ c = (SequenceNode) c.right();\r
+ } else {\r
+ if (c.left() != null) {\r
+ // Dummy node for polytomy - keeps c.left free for new node\r
+ SequenceNode tmpn = new SequenceNode(null, c, null, 0, 0, true);\r
+ tmpn.SetChildren(c.left(), c.right());\r
+ c.setRight(tmpn);\r
+ }\r
+\r
+ c.setLeft(new SequenceNode(null, c, null, DefDistance, DefBootstrap,\r
+ false));\r
+ c = (SequenceNode) c.left();\r
+ }\r
+\r
+ if (realroot == null) {\r
+ realroot = c;\r
+ }\r
+\r
+ nodename = null;\r
+ distance = DefDistance;\r
+ bootstrap = DefBootstrap;\r
+ cp = fcp + 1;\r
+\r
+ break;\r
+\r
+ // Deal with quoted fields\r
+ case '\'':\r
+\r
+ Matcher qnodename = Pattern.compile("([^']|'')+'").matcher(nf);\r
+ if (qnodename.find(fcp)) {\r
+ nodename = new String(qnodename.group(1));\r
+ cp = qnodename.end(0);\r
+ } else {\r
+ Error = ErrorStringrange(Error, "Unterminated quotes for nodename",\r
+ 7, fcp, nf);\r
+ }\r
+\r
+ break;\r
+\r
+ case ';':\r
+\r
+ if (d != -1) {\r
+ Error = ErrorStringrange(Error,\r
+ "Wayward semicolon (depth=" + d + ")", 7, fcp, nf);\r
+ }\r
+\r
+ // cp advanced at the end of default\r
+ default:\r
+\r
+ // Parse simpler field strings\r
+ String fstring = nf.substring(cp, fcp);\r
+ Matcher uqnodename = Pattern.compile("\\b([^' :;\\](),]+)").matcher(fstring);\r
+ if (uqnodename.matches()\r
+ && ((uqnodename.start(1) == 0) || (fstring.charAt(uqnodename\r
+ .start(1) - 1) != ':'))) // JBPNote HACK!\r
+ {\r
+ if (nodename == null) {\r
+ if (ReplaceUnderscores) {\r
+ nodename = uqnodename.group(1).replace('_', ' ');\r
+ } else {\r
+ nodename = uqnodename.group(1);\r
+ }\r
+ } else {\r
+ Error = ErrorStringrange(Error,\r
+ "File has broken algorithm - overwritten nodename", 10, fcp, nf);\r
+ }\r
+ }\r
+ \r
+ Matcher nbootstrap = Pattern.compile("\\S+([0-9+]+)\\S*:").matcher(fstring);\r
+\r
+\r
+ if (nbootstrap.matches()\r
+ && (nbootstrap.start(1) > uqnodename.end(1))) {\r
+ try {\r
+ bootstrap = (new Integer(nbootstrap.group(1))).intValue();\r
+ HasBootstrap = true;\r
+ } catch (Exception e) {\r
+ Error = ErrorStringrange(Error, "Can't parse bootstrap value", 4,\r
+ cp + nbootstrap.start(0), nf);\r
+ }\r
+ }\r
+\r
+ Matcher ndist = Pattern.compile(\r
+ ":([-0-9Ee.+]+)").matcher(fstring);\r
+ boolean nodehasdistance = false;\r
+\r
+ if (ndist.matches()) {\r
+ try {\r
+ distance = (new Float(ndist.group(1))).floatValue();\r
+ HasDistances = true;\r
+ nodehasdistance = true;\r
+ } catch (Exception e) {\r
+ Error = ErrorStringrange(Error, "Can't parse node distance value",\r
+ 7, cp + ndist.start(0), nf);\r
+ }\r
+ }\r
+\r
+ if (ascending) {\r
+ // Write node info here\r
+ c.setName(nodename);\r
+ // Trees without distances still need a render distance\r
+ c.dist = (HasDistances) ? distance : DefDistance;\r
+ // be consistent for internal bootstrap defaults too\r
+ c.setBootstrap((HasBootstrap) ? bootstrap : DefBootstrap);\r
+ if (c == realroot) {\r
+ RootHasDistance = nodehasdistance; // JBPNote This is really\r
+ // UGLY!!! Ensure root node gets\r
+ // its given distance\r
+ }\r
+ } else {\r
+ // Find a place to put the leaf\r
+ SequenceNode newnode = new SequenceNode(null, c, nodename,\r
+ (HasDistances) ? distance : DefDistance,\r
+ (HasBootstrap) ? bootstrap : DefBootstrap, false);\r
+\r
+ if (c.right() == null) {\r
+ c.setRight(newnode);\r
+ } else {\r
+ if (c.left() == null) {\r
+ c.setLeft(newnode);\r
+ } else {\r
+ // Insert a dummy node for polytomy\r
+ // dummy nodes have distances\r
+ SequenceNode newdummy = new SequenceNode(null, c, null,\r
+ (HasDistances ? 0 : DefDistance), 0, true);\r
+ newdummy.SetChildren(c.left(), newnode);\r
+ c.setLeft(newdummy);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (ascending) {\r
+ // move back up the tree from preceding closure\r
+ c = c.AscendTree();\r
+\r
+ if ((d > -1) && (c == null)) {\r
+ Error = ErrorStringrange(\r
+ Error,\r
+ "File broke algorithm: Lost place in tree (is there an extra ')' ?)",\r
+ 7, fcp, nf);\r
+ }\r
+ }\r
+\r
+ if (nf.charAt(fcp) == ')') {\r
+ d--;\r
+ ascending = true;\r
+ } else {\r
+ if (nf.charAt(fcp) == ',') {\r
+ if (ascending) {\r
+ ascending = false;\r
+ } else {\r
+ // Just advance focus, if we need to\r
+ if ((c.left() != null) && (!c.left().isLeaf())) {\r
+ c = (SequenceNode) c.left();\r
+ }\r
+ }\r
+ }\r
+\r
+ // else : We do nothing if ';' is encountered.\r
+ }\r
+\r
+ // Reset new node properties to obvious fakes\r
+ nodename = null;\r
+ distance = DefDistance;\r
+ bootstrap = DefBootstrap;\r
+\r
+ cp = fcp + 1;\r
+ }\r
+ }\r
+\r
+ if (Error != null) {\r
+ throw (new IOException("NewickFile: " + Error + "\n"));\r
+ }\r
+\r
+ root = (SequenceNode) root.right().detach(); // remove the imaginary root.\r
+\r
+ if (!RootHasDistance) {\r
+ root.dist = (HasDistances) ? 0 : DefDistance;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ public SequenceNode getTree() {\r
+ return root;\r
+ }\r
+ public uk.ac.vamsas.objects.core.Treenode[] matchTreeNodeNames(String[] names, Vobject[] boundObjects)\r
+ {\r
+ // todo!\r
+ // also - need to reconstruct a names object id mapping (or BInaryNode) mapping for the parsed tree file\r
+ return null;\r
+ }\r
+ /**\r
+ * Generate a newick format tree according to internal flags for bootstraps,\r
+ * distances and root distances.\r
+ * \r
+ * @return new hampshire tree in a single line\r
+ */\r
+ public String print() {\r
+ synchronized (this) {\r
+ StringBuffer tf = new StringBuffer();\r
+ print(tf, root);\r
+\r
+ return (tf.append(";").toString());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * \r
+ * Generate a newick format tree according to internal flags for distances and\r
+ * root distances and user specificied writing of bootstraps.\r
+ * \r
+ * @param withbootstraps\r
+ * controls if bootstrap values are explicitly written.\r
+ * \r
+ * @return new hampshire tree in a single line\r
+ */\r
+ public String print(boolean withbootstraps) {\r
+ synchronized (this) {\r
+ boolean boots = this.HasBootstrap;\r
+ this.HasBootstrap = withbootstraps;\r
+\r
+ String rv = print();\r
+ this.HasBootstrap = boots;\r
+\r
+ return rv;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * Generate newick format tree according to internal flags for writing root\r
+ * node distances.\r
+ * \r
+ * @param withbootstraps\r
+ * explicitly write bootstrap values\r
+ * @param withdists\r
+ * explicitly write distances\r
+ * \r
+ * @return new hampshire tree in a single line\r
+ */\r
+ public String print(boolean withbootstraps, boolean withdists) {\r
+ synchronized (this) {\r
+ boolean dists = this.HasDistances;\r
+ this.HasDistances = withdists;\r
+\r
+ String rv = print(withbootstraps);\r
+ this.HasDistances = dists;\r
+\r
+ return rv;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Generate newick format tree according to user specified flags\r
+ * \r
+ * @param withbootstraps\r
+ * explicitly write bootstrap values\r
+ * @param withdists\r
+ * explicitly write distances\r
+ * @param printRootInfo\r
+ * explicitly write root distance\r
+ * \r
+ * @return new hampshire tree in a single line\r
+ */\r
+ public String print(boolean withbootstraps, boolean withdists,\r
+ boolean printRootInfo) {\r
+ synchronized (this) {\r
+ boolean rootinfo = printRootInfo;\r
+ this.printRootInfo = printRootInfo;\r
+\r
+ String rv = print(withbootstraps, withdists);\r
+ this.printRootInfo = rootinfo;\r
+\r
+ return rv;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ char getQuoteChar() {\r
+ return QuoteChar;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param c\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ char setQuoteChar(char c) {\r
+ char old = QuoteChar;\r
+ QuoteChar = c;\r
+\r
+ return old;\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param name\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ private String nodeName(String name) {\r
+ if (NodeSafeName[0].matcher(name).find()) {\r
+ return QuoteChar + NodeSafeName[1].matcher(name).replaceAll("''") + QuoteChar; // quite \r
+ } else {\r
+ return NodeSafeName[2].matcher(name).replaceAll("_"); // whitespace\r
+ }\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param c\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ private String printNodeField(SequenceNode c) {\r
+ return c.getNewickNodeName()\r
+ + ((HasBootstrap) ? ((c.getBootstrap() > -1) ? (" " + c.getBootstrap())\r
+ : "") : "") + ((HasDistances) ? (":" + c.dist) : "");\r
+ }\r
+\r
+ /**\r
+ * DOCUMENT ME!\r
+ * \r
+ * @param root\r
+ * DOCUMENT ME!\r
+ * \r
+ * @return DOCUMENT ME!\r
+ */\r
+ private String printRootField(SequenceNode root) {\r
+ return (printRootInfo) ? (((root.getName() == null) ? "" : nodeName(root\r
+ .getName()))\r
+ + ((HasBootstrap) ? ((root.getBootstrap() > -1) ? (" " + root\r
+ .getBootstrap()) : "") : "") + ((RootHasDistance) ? (":" + root.dist)\r
+ : ""))\r
+ : "";\r
+ }\r
+\r
+ // Non recursive call deals with root node properties\r
+ public void print(StringBuffer tf, SequenceNode root) {\r
+ if (root != null) {\r
+ if (root.isLeaf() && printRootInfo) {\r
+ tf.append(printRootField(root));\r
+ } else {\r
+ if (root.isDummy()) {\r
+ _print(tf, (SequenceNode) root.right());\r
+ _print(tf, (SequenceNode) root.left());\r
+ } else {\r
+ tf.append("(");\r
+ _print(tf, (SequenceNode) root.right());\r
+\r
+ if (root.left() != null) {\r
+ tf.append(",");\r
+ }\r
+\r
+ _print(tf, (SequenceNode) root.left());\r
+ tf.append(")" + printRootField(root));\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Recursive call for non-root nodes\r
+ public void _print(StringBuffer tf, SequenceNode c) {\r
+ if (c != null) {\r
+ if (c.isLeaf()) {\r
+ tf.append(printNodeField(c));\r
+ } else {\r
+ if (c.isDummy()) {\r
+ _print(tf, (SequenceNode) c.left());\r
+ if (c.left() != null) {\r
+ tf.append(",");\r
+ }\r
+ _print(tf, (SequenceNode) c.right());\r
+ } else {\r
+ tf.append("(");\r
+ _print(tf, (SequenceNode) c.right());\r
+\r
+ if (c.left() != null) {\r
+ tf.append(",");\r
+ }\r
+\r
+ _print(tf, (SequenceNode) c.left());\r
+ tf.append(")" + printNodeField(c));\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Test\r
+ public static void main(String[] args) {\r
+ try {\r
+ if (args == null || args.length != 1) {\r
+ System.err\r
+ .println("Takes one argument - file name of a newick tree file.");\r
+ System.exit(0);\r
+ }\r
+\r
+ File fn = new File(args[0]);\r
+\r
+ StringBuffer newickfile = new StringBuffer();\r
+ BufferedReader treefile = new BufferedReader(new FileReader(fn));\r
+ String l;\r
+\r
+ while ((l = treefile.readLine()) != null) {\r
+ newickfile.append(l);\r
+ }\r
+\r
+ treefile.close();\r
+ System.out.println("Read file :\n");\r
+ NewickFile trf = new NewickFile(fn);\r
+ // trf.parse();\r
+ System.out.println("Original file :\n");\r
+\r
+ System.out.println(Pattern.compile("\n+").matcher(newickfile.toString()).replaceAll("") + "\n");\r
+\r
+ System.out.println("Parsed file.\n");\r
+ System.out.println("Default output type for original input.\n");\r
+ System.out.println(trf.print());\r
+ System.out.println("Without bootstraps.\n");\r
+ System.out.println(trf.print(false));\r
+ System.out.println("Without distances.\n");\r
+ System.out.println(trf.print(true, false));\r
+ System.out.println("Without bootstraps but with distanecs.\n");\r
+ System.out.println(trf.print(false, true));\r
+ System.out.println("Without bootstraps or distanecs.\n");\r
+ System.out.println(trf.print(false, false));\r
+ System.out.println("With bootstraps and with distances.\n");\r
+ System.out.println(trf.print(true, true));\r
+ } catch (java.io.IOException e) {\r
+ System.err.println("Exception\n" + e);\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+}\r