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