Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.io

File NewickFile.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
30% of files have more coverage

Code metrics

144
316
28
1
1,090
677
128
0.41
11.29
28
4.57

Classes

Class Line # Actions
NewickFile 82 316 128
0.6372950763.7%
 

Contributing tests

This file is covered by 16 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    // NewickFile.java
22    // Tree I/O
23    // http://evolution.genetics.washington.edu/phylip/newick_doc.html
24    // TODO: Implement Basic NHX tag parsing and preservation
25    // TODO: http://evolution.genetics.wustl.edu/eddy/forester/NHX.html
26    // TODO: Extended SequenceNodeI to hold parsed NHX strings
27    package jalview.io;
28   
29    import java.io.BufferedReader;
30    import java.io.File;
31    import java.io.FileReader;
32    import java.io.IOException;
33    import java.util.Locale;
34    import java.util.StringTokenizer;
35   
36    import com.stevesoft.pat.Regex;
37   
38    import jalview.bin.Jalview;
39    import jalview.bin.Jalview.ExitCode;
40    import jalview.datamodel.BinaryNode;
41    import jalview.datamodel.SequenceNode;
42    import jalview.util.MessageManager;
43    import jalview.util.Platform;
44   
45    /**
46    * Parse a new hanpshire style tree Caveats: NHX files are NOT supported and the
47    * tree distances and topology are unreliable when they are parsed. TODO: on
48    * this: NHX codes are appended in comments beginning with &&NHX. The codes are
49    * given below (from http://www.phylosoft.org/forester/NHX.html): Element Type
50    * Description Corresponding phyloXML element (parent element in parentheses) no
51    * tag string name of this node/clade (MUST BE FIRST, IF ASSIGNED)
52    * <name>(<clade>) : decimal branch length to parent node (MUST BE SECOND, IF
53    * ASSIGNED) <branch_length>(<clade>) :GN= string gene name <name>(<sequence>)
54    * :AC= string sequence accession <accession>(<sequence>) :ND= string node
55    * identifier - if this is being used, it has to be unique within each phylogeny
56    * <node_id>(<clade>) :B= decimal confidence value for parent branch
57    * <confidence>(<clade>) :D= 'T', 'F', or '?' 'T' if this node represents a
58    * duplication event - 'F' if this node represents a speciation event, '?' if
59    * this node represents an unknown event (D= tag should be replaced by Ev= tag)
60    * n/a :Ev=duplications>speciations>gene losses>event type>duplication type int
61    * int int string string event (replaces the =D tag), number of duplication,
62    * speciation, and gene loss events, type of event (transfer, fusion, root,
63    * unknown, other, speciation_duplication_loss, unassigned) <events>(<clade>)
64    * :E= string EC number at this node <annotation>(<sequence>) :Fu= string
65    * function at this node <annotation>(<sequence>)
66    * :DS=protein-length>from>to>support>name>from>... int int int double string
67    * int ... domain structure at this node <domain_architecture>(<sequence>) :S=
68    * string species name of the species/phylum at this node <taxonomy>(<clade>)
69    * :T= integer taxonomy ID of the species/phylum at this node <id>(<taxonomy>)
70    * :W= integer width of parent branch <width>(<clade>) :C=rrr.ggg.bbb
71    * integer.integer.integer color of parent branch <color>(<clade>) :Co= 'Y' or
72    * 'N' collapse this node when drawing the tree (default is not to collapse) n/a
73    * :XB= string custom data associated with a branch <property>(<clade>) :XN=
74    * string custom data associated with a node <property>(<clade>) :O= integer
75    * orthologous to this external node n/a :SN= integer subtree neighbors n/a :SO=
76    * integer super orthologous (no duplications on paths) to this external node
77    * n/a
78    *
79    * @author Jim Procter
80    * @version $Revision$
81    */
 
82    public class NewickFile extends FileParse
83    {
84    BinaryNode root;
85   
86    private boolean HasBootstrap = false;
87   
88    private boolean HasDistances = false;
89   
90    private boolean RootHasDistance = false;
91   
92    // File IO Flags
93    private boolean ReplaceUnderscores = false;
94   
95    private boolean printRootInfo = true;
96   
97    private static final int REGEX_PERL_NODE_REQUIRE_QUOTE = 0;
98   
99    private static final int REGEX_PERL_NODE_ESCAPE_QUOTE = 1;
100   
101    private static final int REGEX_PERL_NODE_UNQUOTED_WHITESPACE = 2;
102   
103    private static final int REGEX_MAJOR_SYMS = 3;
104   
105    private static final int REGEX_QNODE_NAME = 4;
106   
107    private static final int REGEX_COMMENT = 5;
108   
109    private static final int REGEX_UQNODE_NAME = 6;
110   
111    private static final int REGEX_NBOOTSTRAP = 7;
112   
113    private static final int REGEX_NDIST = 8;
114   
115    private static final int REGEX_NO_LINES = 9;
116   
117    private static final int REGEX_PERL_EXPAND_QUOTES = 10;
118   
119    private static final int REGEX_MAX = 11;
120   
121    private static final Regex[] REGEX = new Regex[REGEX_MAX];
122   
 
123  1736 toggle private static Regex getRegex(int id)
124    {
125  1736 if (REGEX[id] == null)
126    {
127  1736 String code = null;
128  1736 String code2 = null;
129  1736 String codePerl = null;
130  1736 switch (id)
131    {
132  155 case REGEX_PERL_NODE_REQUIRE_QUOTE:
133  155 codePerl = "m/[\\[,:'()]/";
134  155 break;
135  4 case REGEX_PERL_NODE_ESCAPE_QUOTE:
136  4 codePerl = "s/'/''/";
137  4 break;
138  151 case REGEX_PERL_NODE_UNQUOTED_WHITESPACE:
139  151 codePerl = "s/\\/w/_/";
140  151 break;
141  8 case REGEX_PERL_EXPAND_QUOTES:
142  8 codePerl = "s/''/'/";
143  8 break;
144  21 case REGEX_MAJOR_SYMS:
145  21 code = "[(\\['),;]";
146  21 break;
147  8 case REGEX_QNODE_NAME:
148  8 code = "'([^']|'')+'";
149  8 break;
150  0 case REGEX_COMMENT:
151  0 code = "]";
152  0 break;
153  463 case REGEX_UQNODE_NAME:
154  463 code = "\\b([^' :;\\](),]+)";
155  463 break;
156  463 case REGEX_NBOOTSTRAP:
157  463 code = "\\s*([0-9+]+)\\s*:";
158  463 break;
159  463 case REGEX_NDIST:
160  463 code = ":([-0-9Ee.+]+)";
161  463 break;
162  0 case REGEX_NO_LINES:
163  0 code = "\n+";
164  0 code2 = "";
165  0 break;
166  0 default:
167  0 return null;
168    }
169  1736 return codePerl == null ? Platform.newRegex(code, code2)
170    : Platform.newRegexPerl(codePerl);
171    }
172  0 return REGEX[id];
173    }
174   
175   
176    private char quoteChar = '\'';
177   
178    /**
179    * Creates a new NewickFile object.
180    *
181    * @param inStr
182    * DOCUMENT ME!
183    *
184    * @throws IOException
185    * DOCUMENT ME!
186    */
 
187  13 toggle public NewickFile(String inStr) throws IOException
188    {
189  13 super(inStr, DataSourceType.PASTE);
190    }
191   
192    /**
193    * Creates a new NewickFile object.
194    *
195    * @param inFile
196    * DOCUMENT ME!
197    * @param protocol
198    * DOCUMENT ME!
199    *
200    * @throws IOException
201    * DOCUMENT ME!
202    */
 
203  8 toggle public NewickFile(String inFile, DataSourceType protocol)
204    throws IOException
205    {
206  8 super(inFile, protocol);
207    }
208   
 
209  0 toggle public NewickFile(FileParse source) throws IOException
210    {
211  0 super(source);
212    }
213   
214    /**
215    * Creates a new NewickFile object.
216    *
217    * @param newtree
218    * DOCUMENT ME!
219    */
 
220  7 toggle public NewickFile(BinaryNode newtree)
221    {
222  7 root = newtree;
223    }
224   
225    /**
226    * Creates a new NewickFile object.
227    *
228    * @param newtree
229    * DOCUMENT ME!
230    * @param bootstrap
231    * DOCUMENT ME!
232    */
 
233  0 toggle public NewickFile(SequenceNode newtree, boolean bootstrap)
234    {
235  0 HasBootstrap = bootstrap;
236  0 root = newtree;
237    }
238   
239    /**
240    * Creates a new NewickFile object.
241    *
242    * @param newtree
243    * DOCUMENT ME!
244    * @param bootstrap
245    * DOCUMENT ME!
246    * @param distances
247    * DOCUMENT ME!
248    */
 
249  3 toggle public NewickFile(BinaryNode newtree, boolean bootstrap,
250    boolean distances)
251    {
252  3 root = newtree;
253  3 HasBootstrap = bootstrap;
254  3 HasDistances = distances;
255    }
256   
257    /**
258    * Creates a new NewickFile object.
259    *
260    * @param newtree
261    * DOCUMENT ME!
262    * @param bootstrap
263    * DOCUMENT ME!
264    * @param distances
265    * DOCUMENT ME!
266    * @param rootdistance
267    * DOCUMENT ME!
268    */
 
269  0 toggle public NewickFile(BinaryNode newtree, boolean bootstrap,
270    boolean distances, boolean rootdistance)
271    {
272  0 root = newtree;
273  0 HasBootstrap = bootstrap;
274  0 HasDistances = distances;
275  0 RootHasDistance = rootdistance;
276    }
277   
278    /**
279    * DOCUMENT ME!
280    *
281    * @param Error
282    * DOCUMENT ME!
283    * @param Er
284    * DOCUMENT ME!
285    * @param r
286    * DOCUMENT ME!
287    * @param p
288    * DOCUMENT ME!
289    * @param s
290    * DOCUMENT ME!
291    *
292    * @return DOCUMENT ME!
293    */
 
294  0 toggle private String ErrorStringrange(String Error, String Er, int r, int p,
295    String s)
296    {
297  0 return ((Error == null) ? "" : Error) + Er + " at position " + p + " ( "
298  0 + s.substring(((p - r) < 0) ? 0 : (p - r),
299  0 ((p + r) > s.length()) ? s.length() : (p + r))
300    + " )\n";
301    }
302   
303    // @tree annotations
304    // These are set automatically by the reader
 
305  39 toggle public boolean HasBootstrap()
306    {
307  39 return HasBootstrap;
308    }
309   
310    /**
311    * DOCUMENT ME!
312    *
313    * @return DOCUMENT ME!
314    */
 
315  39 toggle public boolean HasDistances()
316    {
317  39 return HasDistances;
318    }
319   
 
320  21 toggle public boolean HasRootDistance()
321    {
322  21 return RootHasDistance;
323    }
324   
325    /**
326    * parse the filesource as a newick file (new hampshire and/or extended)
327    *
328    * @throws IOException
329    * with a line number and character position for badly formatted NH
330    * strings
331    */
 
332  21 toggle public void parse() throws IOException
333    {
334  21 Platform.ensureRegex();
335  21 String nf;
336   
337    { // fill nf with complete tree file
338   
339  21 StringBuffer file = new StringBuffer();
340   
341  ? while ((nf = nextLine()) != null)
342    {
343  21 file.append(nf);
344    }
345   
346  21 nf = file.toString();
347    }
348   
349  21 root = new SequenceNode();
350   
351  21 BinaryNode realroot = null;
352  21 BinaryNode c = root;
353   
354  21 int d = -1;
355  21 int cp = 0;
356    // int flen = nf.length();
357   
358  21 String Error = null;
359  21 String nodename = null;
360  21 String commentString2 = null; // comments after simple node props
361   
362  21 double DefDistance = (float) 0.001; // @param Default distance for a node -
363    // very very small
364  21 int DefBootstrap = -1; // @param Default bootstrap for a node
365   
366  21 double distance = DefDistance;
367  21 int bootstrap = DefBootstrap;
368   
369  21 boolean ascending = false; // flag indicating that we are leaving the
370    // current node
371   
372  21 Regex majorsyms = getRegex(REGEX_MAJOR_SYMS); // "[(\\['),;]"
373   
374  21 int nextcp = 0;
375  21 int ncp = cp;
376  21 boolean parsednodename = false;
377  713 while (majorsyms.searchFrom(nf, cp) && (Error == null))
378    {
379  692 int fcp = majorsyms.matchedFrom();
380  692 char schar;
381  692 switch (schar = nf.charAt(fcp))
382    {
383  221 case '(':
384   
385    // ascending should not be set
386    // New Internal node
387  221 if (ascending)
388    {
389  0 Error = ErrorStringrange(Error, "Unexpected '('", 7, fcp, nf);
390   
391  0 continue;
392    }
393  221 d++;
394   
395  221 if (c.right() == null)
396    {
397  99 c.setRight(new SequenceNode(null, c, null, DefDistance,
398    DefBootstrap, false));
399  99 c = c.right();
400    }
401    else
402    {
403  122 if (c.left() != null)
404    {
405    // Dummy node for polytomy - keeps c.left free for new node
406  0 BinaryNode tmpn = new SequenceNode(null, c, null, 0, 0, true);
407  0 tmpn.SetChildren(c.left(), c.right());
408  0 c.setRight(tmpn);
409    }
410   
411  122 c.setLeft(new SequenceNode(null, c, null, DefDistance,
412    DefBootstrap, false));
413  122 c = c.left();
414    }
415   
416  221 if (realroot == null)
417    {
418  21 realroot = c;
419    }
420   
421  221 nodename = null;
422  221 distance = DefDistance;
423  221 bootstrap = DefBootstrap;
424  221 cp = fcp + 1;
425   
426  221 break;
427   
428    // Deal with quoted fields
429  8 case '\'':
430   
431  8 Regex qnodename = getRegex(REGEX_QNODE_NAME);// "'([^']|'')+'");
432   
433  8 if (qnodename.searchFrom(nf, fcp))
434    {
435  8 int nl = qnodename.stringMatched().length();
436  8 nodename = new String(
437    qnodename.stringMatched().substring(1, nl - 1));
438    // unpack any escaped colons
439  8 Regex xpandquotes = getRegex(REGEX_PERL_EXPAND_QUOTES);
440  8 String widernodename = xpandquotes.replaceAll(nodename);
441  8 nodename = widernodename;
442    // jump to after end of quoted nodename
443  8 nextcp = fcp + nl + 1;
444  8 parsednodename = true;
445    }
446    else
447    {
448  0 Error = ErrorStringrange(Error,
449    "Unterminated quotes for nodename", 7, fcp, nf);
450    }
451   
452  8 break;
453   
454  463 default:
455  463 if (schar == ';')
456    {
457  21 if (d != -1)
458    {
459  0 Error = ErrorStringrange(Error,
460    "Wayward semicolon (depth=" + d + ")", 7, fcp, nf);
461    }
462    // cp advanced at the end of default
463    }
464  463 if (schar == '[')
465    {
466    // node string contains Comment or structured/extended NH format info
467    /*
468    * if ((fcp-cp>1 && nf.substring(cp,fcp).trim().length()>1)) { // will
469    * process in remains jalview.bin.Console.errPrintln("skipped text:
470    * '"+nf.substring(cp,fcp)+"'"); }
471    */
472    // verify termination.
473  0 Regex comment = getRegex(REGEX_COMMENT); // "]"
474  0 if (comment.searchFrom(nf, fcp))
475    {
476    // Skip the comment field
477  0 nextcp = comment.matchedFrom() + 1;
478  0 warningMessage = "Tree file contained comments which may confuse input algorithm.";
479  0 break;
480   
481    // cp advanced at the end of default to nextcp, ncp is unchanged so
482    // any node info can be read.
483    }
484    else
485    {
486  0 Error = ErrorStringrange(Error, "Unterminated comment", 3, fcp,
487    nf);
488    }
489    }
490    // Parse simpler field strings
491  463 String fstring = nf.substring(ncp, fcp);
492    // remove any comments before we parse the node info
493    // TODO: test newick file with quoted square brackets in node name (is
494    // this allowed?)
495  463 while (fstring.indexOf(']') > -1)
496    {
497  0 int cstart = fstring.indexOf('[');
498  0 int cend = fstring.indexOf(']');
499  0 commentString2 = fstring.substring(cstart + 1, cend);
500  0 fstring = fstring.substring(0, cstart)
501    + fstring.substring(cend + 1);
502   
503    }
504  463 Regex uqnodename = getRegex(REGEX_UQNODE_NAME);// "\\b([^' :;\\](),]+)"
505  463 Regex nbootstrap = getRegex(REGEX_NBOOTSTRAP);// "\\s*([0-9+]+)\\s*:");
506  463 Regex ndist = getRegex(REGEX_NDIST);// ":([-0-9Ee.+]+)");
507   
508  463 if (!parsednodename && uqnodename.search(fstring)
509    && ((uqnodename.matchedFrom(1) == 0) || (fstring
510    .charAt(uqnodename.matchedFrom(1) - 1) != ':'))) // JBPNote
511    // HACK!
512    {
513  234 if (nodename == null)
514    {
515  234 if (ReplaceUnderscores)
516    {
517  0 nodename = uqnodename.stringMatched(1).replace('_', ' ');
518    }
519    else
520    {
521  234 nodename = uqnodename.stringMatched(1);
522    }
523    }
524    else
525    {
526  0 Error = ErrorStringrange(Error,
527    "File has broken algorithm - overwritten nodename", 10,
528    fcp, nf);
529    }
530    }
531    // get comment bootstraps
532   
533  463 if (nbootstrap.search(fstring))
534    {
535  36 if (nbootstrap.stringMatched(1)
536    .equals(uqnodename.stringMatched(1)))
537    {
538  0 nodename = null; // no nodename here.
539    }
540  36 if (nodename == null || nodename.length() == 0
541    || nbootstrap.matchedFrom(1) > (uqnodename.matchedFrom(1)
542    + uqnodename.stringMatched().length()))
543    {
544  0 try
545    {
546  0 bootstrap = (Integer.valueOf(nbootstrap.stringMatched(1)))
547    .intValue();
548  0 HasBootstrap = true;
549    } catch (Exception e)
550    {
551  0 Error = ErrorStringrange(Error, "Can't parse bootstrap value",
552    4, ncp + nbootstrap.matchedFrom(), nf);
553    }
554    }
555    }
556   
557  463 boolean nodehasdistance = false;
558   
559  463 if (ndist.search(fstring))
560    {
561  442 try
562    {
563  442 distance = (Double.valueOf(ndist.stringMatched(1)))
564    .floatValue();
565  442 HasDistances = true;
566  442 nodehasdistance = true;
567    } catch (Exception e)
568    {
569  0 Error = ErrorStringrange(Error,
570    "Can't parse node distance value", 7,
571    ncp + ndist.matchedFrom(), nf);
572    }
573    }
574   
575  463 if (ascending)
576    {
577    // Write node info here
578  221 c.setName(nodename);
579    // Trees without distances still need a render distance
580  221 c.dist = (HasDistances) ? distance : DefDistance;
581    // be consistent for internal bootstrap defaults too
582  221 c.setBootstrap((HasBootstrap) ? bootstrap : DefBootstrap);
583  221 if (c == realroot)
584    {
585  21 RootHasDistance = nodehasdistance; // JBPNote This is really
586    // UGLY!!! Ensure root node gets
587    // its given distance
588    }
589  221 parseNHXNodeProps(c, commentString2);
590  221 commentString2 = null;
591    }
592    else
593    {
594    // Find a place to put the leaf
595  242 BinaryNode newnode = new SequenceNode(null, c, nodename,
596  242 (HasDistances) ? distance : DefDistance,
597  242 (HasBootstrap) ? bootstrap : DefBootstrap, false);
598  242 parseNHXNodeProps(c, commentString2);
599  242 commentString2 = null;
600   
601  242 if (c.right() == null)
602    {
603  143 c.setRight(newnode);
604    }
605    else
606    {
607  99 if (c.left() == null)
608    {
609  99 c.setLeft(newnode);
610    }
611    else
612    {
613    // Insert a dummy node for polytomy
614    // dummy nodes have distances
615  0 BinaryNode newdummy = new SequenceNode(null, c, null,
616  0 (HasDistances ? 0 : DefDistance), 0, true);
617  0 newdummy.SetChildren(c.left(), newnode);
618  0 c.setLeft(newdummy);
619    }
620    }
621    }
622   
623  463 if (ascending)
624    {
625    // move back up the tree from preceding closure
626  221 c = c.AscendTree();
627   
628  221 if ((d > -1) && (c == null))
629    {
630  0 Error = ErrorStringrange(Error,
631    "File broke algorithm: Lost place in tree (is there an extra ')' ?)",
632    7, fcp, nf);
633    }
634    }
635   
636  463 if (nf.charAt(fcp) == ')')
637    {
638  221 d--;
639  221 ascending = true;
640    }
641    else
642    {
643  242 if (nf.charAt(fcp) == ',')
644    {
645  221 if (ascending)
646    {
647  78 ascending = false;
648    }
649    else
650    {
651    // Just advance focus, if we need to
652  143 if ((c.left() != null) && (!c.left().isLeaf()))
653    {
654  0 c = c.left();
655    }
656    }
657    }
658    }
659   
660    // Reset new node properties to obvious fakes
661  463 nodename = null;
662  463 distance = DefDistance;
663  463 bootstrap = DefBootstrap;
664  463 commentString2 = null;
665  463 parsednodename = false;
666    }
667  692 if (nextcp == 0)
668    {
669  684 ncp = cp = fcp + 1;
670    }
671    else
672    {
673  8 cp = nextcp;
674  8 nextcp = 0;
675    }
676    }
677   
678  21 if (Error != null)
679    {
680  0 throw (new IOException(
681    MessageManager.formatMessage("exception.newfile", new String[]
682    { Error.toString() })));
683    }
684  21 if (root == null)
685    {
686  0 throw (new IOException(
687    MessageManager.formatMessage("exception.newfile", new String[]
688    { MessageManager.getString("label.no_tree_read_in") })));
689    }
690    // THe next line is failing for topali trees - not sure why yet. if
691    // (root.right()!=null && root.isDummy())
692  21 root = root.right().detach(); // remove the imaginary root.
693   
694  21 if (!RootHasDistance)
695    {
696  21 root.dist = (HasDistances) ? 0 : DefDistance;
697    }
698    }
699   
700    /**
701    * parse NHX codes in comment strings and update NewickFile state flags for
702    * distances and bootstraps, and add any additional properties onto the node.
703    *
704    * @param c
705    * @param commentString
706    * @param commentString2
707    */
 
708  463 toggle private void parseNHXNodeProps(BinaryNode c, String commentString)
709    {
710    // TODO: store raw comment on the sequenceNode so it can be recovered when
711    // tree is output
712  463 if (commentString != null && commentString.startsWith("&&NHX"))
713    {
714  0 StringTokenizer st = new StringTokenizer(commentString.substring(5),
715    ":");
716  0 while (st.hasMoreTokens())
717    {
718  0 String tok = st.nextToken();
719  0 int colpos = tok.indexOf("=");
720   
721  0 if (colpos > -1)
722    {
723  0 String code = tok.substring(0, colpos);
724  0 String value = tok.substring(colpos + 1);
725  0 try
726    {
727    // parse out code/value pairs
728  0 if (code.toLowerCase(Locale.ROOT).equals("b"))
729    {
730  0 int v = -1;
731  0 Float iv = Float.valueOf(value);
732  0 v = iv.intValue(); // jalview only does integer bootstraps
733    // currently
734  0 c.setBootstrap(v);
735  0 HasBootstrap = true;
736    }
737    // more codes here.
738    } catch (Exception e)
739    {
740  0 jalview.bin.Console.errPrintln(
741    "Couldn't parse code '" + code + "' = '" + value + "'");
742  0 e.printStackTrace(System.err);
743    }
744    }
745    }
746    }
747   
748    }
749   
750    /**
751    * DOCUMENT ME!
752    *
753    * @return DOCUMENT ME!
754    */
 
755  48 toggle public BinaryNode getTree()
756    {
757  48 return root;
758    }
759   
760    /**
761    * Generate a newick format tree according to internal flags for bootstraps,
762    * distances and root distances.
763    *
764    * @return new hampshire tree in a single line
765    */
 
766  10 toggle public String print()
767    {
768  10 synchronized (this)
769    {
770  10 StringBuffer tf = new StringBuffer();
771  10 print(tf, root);
772   
773  10 return (tf.append(";").toString());
774    }
775    }
776   
777    /**
778    *
779    *
780    * Generate a newick format tree according to internal flags for distances and
781    * root distances and user specificied writing of bootstraps.
782    *
783    * @param withbootstraps
784    * controls if bootstrap values are explicitly written.
785    *
786    * @return new hampshire tree in a single line
787    */
 
788  7 toggle public String print(boolean withbootstraps)
789    {
790  7 synchronized (this)
791    {
792  7 boolean boots = this.HasBootstrap;
793  7 this.HasBootstrap = withbootstraps;
794   
795  7 String rv = print();
796  7 this.HasBootstrap = boots;
797   
798  7 return rv;
799    }
800    }
801   
802    /**
803    *
804    * Generate newick format tree according to internal flags for writing root
805    * node distances.
806    *
807    * @param withbootstraps
808    * explicitly write bootstrap values
809    * @param withdists
810    * explicitly write distances
811    *
812    * @return new hampshire tree in a single line
813    */
 
814  7 toggle public String print(boolean withbootstraps, boolean withdists)
815    {
816  7 synchronized (this)
817    {
818  7 boolean dists = this.HasDistances;
819  7 this.HasDistances = withdists;
820   
821  7 String rv = print(withbootstraps);
822  7 this.HasDistances = dists;
823   
824  7 return rv;
825    }
826    }
827   
828    /**
829    * Generate newick format tree according to user specified flags
830    *
831    * @param withbootstraps
832    * explicitly write bootstrap values
833    * @param withdists
834    * explicitly write distances
835    * @param printRootInfo
836    * explicitly write root distance
837    *
838    * @return new hampshire tree in a single line
839    */
 
840  2 toggle public String print(boolean withbootstraps, boolean withdists,
841    boolean printRootInfo)
842    {
843  2 synchronized (this)
844    {
845  2 boolean rootinfo = printRootInfo;
846  2 this.printRootInfo = printRootInfo;
847   
848  2 String rv = print(withbootstraps, withdists);
849  2 this.printRootInfo = rootinfo;
850   
851  2 return rv;
852    }
853    }
854   
855    /**
856    * DOCUMENT ME!
857    *
858    * @return DOCUMENT ME!
859    */
 
860  0 toggle char getQuoteChar()
861    {
862  0 return quoteChar;
863    }
864   
865    /**
866    * DOCUMENT ME!
867    *
868    * @param c
869    * DOCUMENT ME!
870    *
871    * @return DOCUMENT ME!
872    */
 
873  0 toggle char setQuoteChar(char c)
874    {
875  0 char old = quoteChar;
876  0 quoteChar = c;
877   
878  0 return old;
879    }
880   
881    /**
882    * DOCUMENT ME!
883    *
884    * @param name
885    * DOCUMENT ME!
886    *
887    * @return DOCUMENT ME!
888    */
 
889  155 toggle private String nodeName(String name)
890    {
891  155 if (getRegex(REGEX_PERL_NODE_REQUIRE_QUOTE).search(name))
892    {
893  4 return quoteChar
894    + getRegex(REGEX_PERL_NODE_ESCAPE_QUOTE).replaceAll(name)
895    + quoteChar;
896    }
897    else
898    {
899  151 return getRegex(REGEX_PERL_NODE_UNQUOTED_WHITESPACE).replaceAll(name);
900    }
901    }
902   
903    /**
904    * Default method for generating a string for a node in a tree - override if
905    * you need to inject custom strings for nodes in a newick file
906    *
907    * @param c
908    * - binary node - may be null !
909    * @return null - or a string to be used for the given node in the exported
910    * newick file
911    */
 
912  449 toggle public String getNodenameFor(BinaryNode c)
913    {
914  449 if (c == null)
915    {
916  0 return null;
917    }
918  449 return c.getName();
919    }
920   
921    /**
922    * DOCUMENT ME!
923    *
924    * @param c
925    * DOCUMENT ME!
926    *
927    * @return DOCUMENT ME!
928    */
 
929  290 toggle private String printNodeField(BinaryNode c)
930    {
931  290 return ((getNodenameFor(c) == null) ? "" : nodeName(getNodenameFor(c)))
932  116 + ((HasBootstrap) ? ((c.getBootstrap() > -1)
933  116 ? ((getNodenameFor(c) != null ? " " : "")
934    + c.getBootstrap())
935    : "") : "")
936  290 + ((HasDistances) ? (":" + c.dist) : "");
937    }
938   
939    /**
940    * DOCUMENT ME!
941    *
942    * @param root
943    * DOCUMENT ME!
944    *
945    * @return DOCUMENT ME!
946    */
 
947  10 toggle private String printRootField(BinaryNode root)
948    {
949  10 return (printRootInfo)
950  8 ? (((getNodenameFor(root) == null) ? ""
951    : nodeName(getNodenameFor(root)))
952  1 + ((HasBootstrap) ? ((root.getBootstrap() > -1)
953  1 ? ((getNodenameFor(root) != null ? " " : "")
954    + +root.getBootstrap())
955    : "") : "")
956  8 + ((RootHasDistance) ? (":" + root.dist) : ""))
957    : "";
958    }
959   
960    // Non recursive call deals with root node properties
 
961  10 toggle public void print(StringBuffer tf, BinaryNode root)
962    {
963  10 if (root != null)
964    {
965  10 if (root.isLeaf() && printRootInfo)
966    {
967  0 tf.append(printRootField(root));
968    }
969    else
970    {
971  10 if (root.isDummy())
972    {
973  0 _print(tf, root.right());
974  0 _print(tf, root.left());
975    }
976    else
977    {
978  10 tf.append("(");
979  10 _print(tf, root.right());
980   
981  10 if (root.left() != null)
982    {
983  10 tf.append(",");
984    }
985   
986  10 _print(tf, root.left());
987  10 tf.append(")" + printRootField(root));
988    }
989    }
990    }
991    }
992   
993    // Recursive call for non-root nodes
 
994  290 toggle public void _print(StringBuffer tf, BinaryNode c)
995    {
996  290 if (c != null)
997    {
998  290 if (c.isLeaf())
999    {
1000  155 tf.append(printNodeField(c));
1001    }
1002    else
1003    {
1004  135 if (c.isDummy())
1005    {
1006  0 _print(tf, c.left());
1007  0 if (c.left() != null)
1008    {
1009  0 tf.append(",");
1010    }
1011  0 _print(tf, c.right());
1012    }
1013    else
1014    {
1015  135 tf.append("(");
1016  135 _print(tf, c.right());
1017   
1018  135 if (c.left() != null)
1019    {
1020  135 tf.append(",");
1021    }
1022   
1023  135 _print(tf, c.left());
1024  135 tf.append(")" + printNodeField(c));
1025    }
1026    }
1027    }
1028    }
1029   
1030    /**
1031    *
1032    * @param args
1033    * @j2sIgnore
1034    */
 
1035  0 toggle public static void main(String[] args)
1036    {
1037  0 try
1038    {
1039  0 if (args == null || args.length != 1)
1040    {
1041  0 Jalview.exit(
1042    "Takes one argument - file name of a newick tree file.",
1043    ExitCode.INVALID_ARGUMENT);
1044    }
1045   
1046  0 File fn = new File(args[0]);
1047   
1048  0 StringBuffer newickfile = new StringBuffer();
1049  0 BufferedReader treefile = new BufferedReader(new FileReader(fn));
1050  0 String l;
1051   
1052  0 while ((l = treefile.readLine()) != null)
1053    {
1054  0 newickfile.append(l);
1055    }
1056   
1057  0 treefile.close();
1058  0 jalview.bin.Console.outPrintln("Read file :\n");
1059   
1060  0 NewickFile trf = new NewickFile(args[0], DataSourceType.FILE);
1061  0 trf.parse();
1062  0 jalview.bin.Console.outPrintln("Original file :\n");
1063   
1064  0 Regex nonl = getRegex(REGEX_NO_LINES);// "\n+", "");
1065  0 jalview.bin.Console
1066    .outPrintln(nonl.replaceAll(newickfile.toString()) + "\n");
1067   
1068  0 jalview.bin.Console.outPrintln("Parsed file.\n");
1069  0 jalview.bin.Console
1070    .outPrintln("Default output type for original input.\n");
1071  0 jalview.bin.Console.outPrintln(trf.print());
1072  0 jalview.bin.Console.outPrintln("Without bootstraps.\n");
1073  0 jalview.bin.Console.outPrintln(trf.print(false));
1074  0 jalview.bin.Console.outPrintln("Without distances.\n");
1075  0 jalview.bin.Console.outPrintln(trf.print(true, false));
1076  0 jalview.bin.Console
1077    .outPrintln("Without bootstraps but with distanecs.\n");
1078  0 jalview.bin.Console.outPrintln(trf.print(false, true));
1079  0 jalview.bin.Console.outPrintln("Without bootstraps or distanecs.\n");
1080  0 jalview.bin.Console.outPrintln(trf.print(false, false));
1081  0 jalview.bin.Console
1082    .outPrintln("With bootstraps and with distances.\n");
1083  0 jalview.bin.Console.outPrintln(trf.print(true, true));
1084    } catch (java.io.IOException e)
1085    {
1086  0 jalview.bin.Console.errPrintln("Exception\n" + e);
1087  0 e.printStackTrace();
1088    }
1089    }
1090    }