be4c24e428db9656fbf38d271006a819315bedcd
[jalview.git] / forester / java / src / org / forester / io / writers / PhylogenyWriter.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: www.phylosoft.org/forester
25
26 package org.forester.io.writers;
27
28 import java.io.BufferedWriter;
29 import java.io.File;
30 import java.io.FileWriter;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.io.StringWriter;
34 import java.io.Writer;
35 import java.util.ArrayList;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Stack;
39
40 import org.forester.io.parsers.nexus.NexusConstants;
41 import org.forester.io.parsers.phyloxml.PhyloXmlMapping;
42 import org.forester.phylogeny.Phylogeny;
43 import org.forester.phylogeny.PhylogenyNode;
44 import org.forester.phylogeny.data.PhylogenyDataUtil;
45 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
46 import org.forester.phylogeny.iterators.PostOrderStackObject;
47 import org.forester.util.ForesterConstants;
48 import org.forester.util.ForesterUtil;
49
50 public final class PhylogenyWriter {
51
52     public final static boolean         INDENT_PHYLOXML_DEAFULT         = true;
53     public final static String          PHYLO_XML_INTENDATION_BASE      = "  ";
54     public final static String          PHYLO_XML_VERSION_ENCODING_LINE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
55     public final static String          PHYLO_XML_NAMESPACE_LINE        = "<phyloxml xmlns:xsi=\""
56                                                                                 + ForesterConstants.XML_SCHEMA_INSTANCE
57                                                                                 + "\" xsi:schemaLocation=\""
58                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
59                                                                                 + " "
60                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
61                                                                                 + "/"
62                                                                                 + ForesterConstants.PHYLO_XML_VERSION
63                                                                                 + "/" + ForesterConstants.PHYLO_XML_XSD
64                                                                                 + "\" " + "xmlns=\""
65                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
66                                                                                 + "\">";
67     public final static String          PHYLO_XML_END                   = "</phyloxml>";
68     private boolean                     _saw_comma;
69     private StringBuffer                _buffer;
70     private Writer                      _writer;
71     private PhylogenyNode               _root;
72     private boolean                     _has_next;
73     private Stack<PostOrderStackObject> _stack;
74     private boolean                     _simple_nh;
75     private boolean                     _nh_write_distance_to_parent;
76     private boolean                     _write_conf_values_in_branckets_in_nh;
77     private boolean                     _indent_phyloxml;
78     private int                         _node_level;
79     private int                         _phyloxml_level;
80     private FORMAT                      _format;
81
82     public PhylogenyWriter() {
83         setIndentPhyloxml( INDENT_PHYLOXML_DEAFULT );
84         setWriteConfidenceValuesInBracketsInNH( false );
85     }
86
87     private void appendPhylogenyLevelPhyloXml( final Writer writer, final Phylogeny tree ) throws IOException {
88         final String indentation = new String();
89         if ( !ForesterUtil.isEmpty( tree.getName() ) ) {
90             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.PHYLOGENY_NAME, tree.getName(), indentation );
91         }
92         if ( tree.getIdentifier() != null ) {
93             if ( ForesterUtil.isEmpty( tree.getIdentifier().getProvider() ) ) {
94                 PhylogenyDataUtil.appendElement( writer,
95                                                  PhyloXmlMapping.IDENTIFIER,
96                                                  tree.getIdentifier().getValue(),
97                                                  indentation );
98             }
99             PhylogenyDataUtil.appendElement( writer,
100                                              PhyloXmlMapping.IDENTIFIER,
101                                              tree.getIdentifier().getValue(),
102                                              PhyloXmlMapping.IDENTIFIER_PROVIDER_ATTR,
103                                              tree.getIdentifier().getProvider(),
104                                              indentation );
105         }
106         if ( !ForesterUtil.isEmpty( tree.getDescription() ) ) {
107             PhylogenyDataUtil.appendElement( writer,
108                                              PhyloXmlMapping.PHYLOGENY_DESCRIPTION,
109                                              tree.getDescription(),
110                                              indentation );
111         }
112         if ( tree.getConfidence() != null ) {
113             if ( ForesterUtil.isEmpty( tree.getConfidence().getType() ) ) {
114                 PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.CONFIDENCE, tree.getConfidence().getValue()
115                         + "", indentation );
116             }
117             PhylogenyDataUtil.appendElement( writer,
118                                              PhyloXmlMapping.CONFIDENCE,
119                                              tree.getConfidence().getValue() + "",
120                                              PhyloXmlMapping.CONFIDENCE_TYPE_ATTR,
121                                              tree.getConfidence().getType(),
122                                              indentation );
123         }
124     }
125
126     private StringBuffer createIndentation() {
127         if ( !isIndentPhyloxml() ) {
128             return null;
129         }
130         final StringBuffer sb = new StringBuffer( getNodeLevel() * 2 );
131         for( int i = 0; i < getNodeLevel(); ++i ) {
132             sb.append( PhylogenyWriter.PHYLO_XML_INTENDATION_BASE );
133         }
134         return sb;
135     }
136
137     private void decreaseNodeLevel() {
138         --_node_level;
139     }
140
141     private StringBuffer getBuffer() {
142         return _buffer;
143     }
144
145     private int getNodeLevel() {
146         return _node_level;
147     }
148
149     private StringBuffer getOutput( final Phylogeny tree ) throws IOException {
150         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
151             throw new RuntimeException( "method inappropriately called" );
152         }
153         if ( tree != null ) {
154             reset( tree );
155             while ( isHasNext() ) {
156                 next();
157             }
158             if ( getOutputFormt() == FORMAT.NH ) {
159                 getBuffer().append( ';' );
160             }
161             return getBuffer();
162         }
163         else {
164             return new StringBuffer( 0 );
165         }
166     }
167
168     private FORMAT getOutputFormt() {
169         return _format;
170     }
171
172     private int getPhyloXmlLevel() {
173         return _phyloxml_level;
174     }
175
176     private PhylogenyNode getRoot() {
177         return _root;
178     }
179
180     private Stack<PostOrderStackObject> getStack() {
181         return _stack;
182     }
183
184     private Writer getWriter() {
185         return _writer;
186     }
187
188     private void increaseNodeLevel() {
189         ++_node_level;
190     }
191
192     private boolean isHasNext() {
193         return _has_next;
194     }
195
196     private boolean isIndentPhyloxml() {
197         return _indent_phyloxml;
198     }
199
200     private boolean isSawComma() {
201         return _saw_comma;
202     }
203
204     private boolean isSimpleNH() {
205         return _simple_nh;
206     }
207
208     private boolean isWriteDistanceToParentInNH() {
209         return _nh_write_distance_to_parent;
210     }
211
212     private void next() throws IOException {
213         while ( true ) {
214             final PostOrderStackObject si = getStack().pop();
215             final PhylogenyNode node = si.getNode();
216             final int phase = si.getPhase();
217             if ( phase > node.getNumberOfDescendants() ) {
218                 setHasNext( node != getRoot() );
219                 if ( ( getOutputFormt() != FORMAT.PHYLO_XML ) || node.isExternal() ) {
220                     if ( !node.isRoot() && node.isFirstChildNode() ) {
221                         increaseNodeLevel();
222                     }
223                     if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
224                         writeNode( node, createIndentation() );
225                     }
226                     else {
227                         writeNode( node, null );
228                     }
229                 }
230                 if ( !node.isRoot() ) {
231                     if ( !node.isLastChildNode() ) {
232                         writeCladeSeparator();
233                     }
234                     else {
235                         writeCloseClade();
236                     }
237                 }
238                 return;
239             }
240             else {
241                 getStack().push( new PostOrderStackObject( node, ( phase + 1 ) ) );
242                 if ( node.isInternal() ) {
243                     getStack().push( new PostOrderStackObject( node.getChildNode( phase - 1 ), 1 ) );
244                     writeOpenClade( node );
245                     if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
246                         if ( phase == 1 ) {
247                             writeNode( node, createIndentation() );
248                         }
249                     }
250                 }
251             }
252         }
253     }
254
255     private void reset( final Phylogeny tree ) {
256         setBuffer( new StringBuffer() );
257         setWriter( null );
258         setSawComma( false );
259         setHasNext( true );
260         setRoot( tree.getRoot() );
261         setStack( new Stack<PostOrderStackObject>() );
262         getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
263         setNodeLevel( 1 );
264     }
265
266     private void reset( final Writer writer, final Phylogeny tree ) {
267         setBuffer( null );
268         setWriter( writer );
269         setSawComma( false );
270         setHasNext( true );
271         setRoot( tree.getRoot() );
272         setStack( new Stack<PostOrderStackObject>() );
273         getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
274         setNodeLevel( 1 );
275     }
276
277     private void setBuffer( final StringBuffer buffer ) {
278         _buffer = buffer;
279     }
280
281     private void setHasNext( final boolean has_next ) {
282         _has_next = has_next;
283     }
284
285     public void setIndentPhyloxml( final boolean indent_phyloxml ) {
286         _indent_phyloxml = indent_phyloxml;
287     }
288
289     private void setNodeLevel( final int level ) {
290         _node_level = level;
291     }
292
293     private void setOutputFormt( final FORMAT format ) {
294         _format = format;
295     }
296
297     private void setPhyloXmlLevel( final int phyloxml_level ) {
298         _phyloxml_level = phyloxml_level;
299     }
300
301     private void setRoot( final PhylogenyNode root ) {
302         _root = root;
303     }
304
305     private void setSawComma( final boolean saw_comma ) {
306         _saw_comma = saw_comma;
307     }
308
309     private void setSimpleNH( final boolean simple_nh ) {
310         _simple_nh = simple_nh;
311     }
312
313     private void setStack( final Stack<PostOrderStackObject> stack ) {
314         _stack = stack;
315     }
316
317     private void setWriteDistanceToParentInNH( final boolean nh_write_distance_to_parent ) {
318         _nh_write_distance_to_parent = nh_write_distance_to_parent;
319     }
320
321     private void setWriter( final Writer writer ) {
322         _writer = writer;
323     }
324
325     public void toNewHampshire( final List<Phylogeny> trees,
326                                 final boolean simple_nh,
327                                 final boolean write_distance_to_parent,
328                                 final File out_file,
329                                 final String separator ) throws IOException {
330         final Iterator<Phylogeny> it = trees.iterator();
331         final StringBuffer sb = new StringBuffer();
332         while ( it.hasNext() ) {
333             sb.append( toNewHampshire( it.next(), simple_nh, write_distance_to_parent ) );
334             sb.append( separator );
335         }
336         writeToFile( sb, out_file );
337     }
338
339     public StringBuffer toNewHampshire( final Phylogeny tree,
340                                         final boolean simple_nh,
341                                         final boolean nh_write_distance_to_parent,
342                                         final boolean write_conf_values_in_branckets_in_nh ) throws IOException {
343         setOutputFormt( FORMAT.NH );
344         setWriteConfidenceValuesInBracketsInNH( write_conf_values_in_branckets_in_nh );
345         setSimpleNH( simple_nh );
346         setWriteDistanceToParentInNH( nh_write_distance_to_parent );
347         return getOutput( tree );
348     }
349
350     public StringBuffer toNewHampshire( final Phylogeny tree,
351                                         final boolean simple_nh,
352                                         final boolean nh_write_distance_to_parent ) throws IOException {
353         setOutputFormt( FORMAT.NH );
354         setSimpleNH( simple_nh );
355         setWriteDistanceToParentInNH( nh_write_distance_to_parent );
356         return getOutput( tree );
357     }
358
359     public void toNewHampshire( final Phylogeny tree,
360                                 final boolean simple_nh,
361                                 final boolean write_distance_to_parent,
362                                 final File out_file ) throws IOException {
363         writeToFile( toNewHampshire( tree, simple_nh, write_distance_to_parent ), out_file );
364     }
365
366     public void toNewHampshire( final Phylogeny tree,
367                                 final boolean simple_nh,
368                                 final boolean write_distance_to_parent,
369                                 final boolean use_brackets_for_confidence,
370                                 final File out_file ) throws IOException {
371         writeToFile( toNewHampshire( tree, simple_nh, write_distance_to_parent, use_brackets_for_confidence ), out_file );
372     }
373
374     public void toNewHampshire( final Phylogeny[] trees,
375                                 final boolean simple_nh,
376                                 final boolean write_distance_to_parent,
377                                 final File out_file,
378                                 final String separator ) throws IOException {
379         final StringBuffer sb = new StringBuffer();
380         for( final Phylogeny element : trees ) {
381             sb.append( toNewHampshire( element, simple_nh, write_distance_to_parent ) );
382             sb.append( separator );
383         }
384         writeToFile( sb, out_file );
385     }
386
387     public void toNewHampshireX( final List<Phylogeny> trees, final File out_file, final String separator )
388             throws IOException {
389         final Iterator<Phylogeny> it = trees.iterator();
390         final StringBuffer sb = new StringBuffer();
391         while ( it.hasNext() ) {
392             sb.append( toNewHampshireX( it.next() ) );
393             sb.append( separator );
394         }
395         writeToFile( sb, out_file );
396     }
397
398     public StringBuffer toNewHampshireX( final Phylogeny tree ) throws IOException {
399         setOutputFormt( FORMAT.NHX );
400         return getOutput( tree );
401     }
402
403     public void toNewHampshireX( final Phylogeny tree, final File out_file ) throws IOException {
404         writeToFile( toNewHampshireX( tree ), out_file );
405     }
406
407     public void toNewHampshireX( final Phylogeny[] trees, final File out_file, final String separator )
408             throws IOException {
409         final StringBuffer sb = new StringBuffer();
410         for( final Phylogeny element : trees ) {
411             sb.append( toNewHampshireX( element ) );
412             sb.append( separator );
413         }
414         writeToFile( sb, out_file );
415     }
416
417     public void toNexus( final File out_file, final List<Phylogeny> trees ) throws IOException {
418         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
419         writeNexusStart( writer );
420         writeNexusTaxaBlock( writer, trees.get( 0 ) );
421         writeNexusTreesBlock( writer, trees );
422         writer.flush();
423         writer.close();
424     }
425
426     public void toNexus( final File out_file, final Phylogeny tree ) throws IOException {
427         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
428         final List<Phylogeny> trees = new ArrayList<Phylogeny>( 1 );
429         trees.add( tree );
430         writeNexusStart( writer );
431         writeNexusTaxaBlock( writer, tree );
432         writeNexusTreesBlock( writer, trees );
433         writer.flush();
434         writer.close();
435     }
436
437     public StringBuffer toNexus( final Phylogeny tree ) throws IOException {
438         final StringWriter string_writer = new StringWriter();
439         final Writer writer = new BufferedWriter( string_writer );
440         final List<Phylogeny> trees = new ArrayList<Phylogeny>( 1 );
441         trees.add( tree );
442         writeNexusStart( writer );
443         writeNexusTaxaBlock( writer, tree );
444         writeNexusTreesBlock( writer, trees );
445         writer.flush();
446         writer.close();
447         return string_writer.getBuffer();
448     }
449
450     public void toPhyloXML( final File out_file,
451                             final List<Phylogeny> trees,
452                             final int phyloxml_level,
453                             final String separator ) throws IOException {
454         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
455         toPhyloXML( writer, trees, phyloxml_level, separator );
456         writer.flush();
457         writer.close();
458     }
459
460     public void toPhyloXML( final File out_file, final Phylogeny tree, final int phyloxml_level ) throws IOException {
461         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
462         writePhyloXmlStart( writer );
463         toPhyloXMLNoPhyloXmlSource( writer, tree, phyloxml_level );
464         writePhyloXmlEnd( writer );
465         writer.flush();
466         writer.close();
467     }
468
469     public StringBuffer toPhyloXML( final Phylogeny tree, final int phyloxml_level ) throws IOException {
470         final StringWriter string_writer = new StringWriter();
471         final Writer writer = new BufferedWriter( string_writer );
472         setPhyloXmlLevel( phyloxml_level );
473         setOutputFormt( FORMAT.PHYLO_XML );
474         writePhyloXmlStart( writer );
475         writeOutput( writer, tree );
476         writePhyloXmlEnd( writer );
477         writer.flush();
478         writer.close();
479         return string_writer.getBuffer();
480     }
481
482     public void toPhyloXML( final Phylogeny[] trees,
483                             final int phyloxml_level,
484                             final File out_file,
485                             final String separator ) throws IOException {
486         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
487         toPhyloXML( writer, trees, phyloxml_level, separator );
488         writer.flush();
489         writer.close();
490     }
491
492     public void toPhyloXML( final Writer writer,
493                             final List<Phylogeny> trees,
494                             final int phyloxml_level,
495                             final String separator ) throws IOException {
496         writePhyloXmlStart( writer );
497         final Iterator<Phylogeny> it = trees.iterator();
498         while ( it.hasNext() ) {
499             toPhyloXMLNoPhyloXmlSource( writer, it.next(), phyloxml_level );
500             writer.write( separator );
501         }
502         writePhyloXmlEnd( writer );
503     }
504
505     public void toPhyloXML( final Writer writer, final Phylogeny tree, final int phyloxml_level ) throws IOException {
506         setPhyloXmlLevel( phyloxml_level );
507         setOutputFormt( FORMAT.PHYLO_XML );
508         writePhyloXmlStart( writer );
509         writeOutput( writer, tree );
510         writePhyloXmlEnd( writer );
511     }
512
513     public void toPhyloXML( final Writer writer,
514                             final Phylogeny[] trees,
515                             final int phyloxml_level,
516                             final String separator ) throws IOException {
517         writePhyloXmlStart( writer );
518         for( final Phylogeny phylogeny : trees ) {
519             toPhyloXMLNoPhyloXmlSource( writer, phylogeny, phyloxml_level );
520             writer.write( separator );
521         }
522         writePhyloXmlEnd( writer );
523     }
524
525     private void toPhyloXMLNoPhyloXmlSource( final Writer writer, final Phylogeny tree, final int phyloxml_level )
526             throws IOException {
527         setPhyloXmlLevel( phyloxml_level );
528         setOutputFormt( FORMAT.PHYLO_XML );
529         writeOutput( writer, tree );
530     }
531
532     private void writeCladeSeparator() {
533         setSawComma( true );
534         if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
535             getBuffer().append( "," );
536         }
537     }
538
539     private void writeCloseClade() throws IOException {
540         decreaseNodeLevel();
541         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
542             getWriter().write( ForesterUtil.LINE_SEPARATOR );
543             if ( isIndentPhyloxml() ) {
544                 getWriter().write( createIndentation().toString() );
545             }
546             PhylogenyDataUtil.appendClose( getWriter(), PhyloXmlMapping.CLADE );
547         }
548         else if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
549             getBuffer().append( ")" );
550         }
551     }
552
553     private void writeNode( final PhylogenyNode node, final StringBuffer indentation ) throws IOException {
554         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
555             if ( node.isExternal() ) {
556                 getWriter().write( ForesterUtil.LINE_SEPARATOR );
557                 if ( indentation != null ) {
558                     getWriter().write( indentation.toString() );
559                 }
560                 PhylogenyDataUtil.appendOpen( getWriter(), PhyloXmlMapping.CLADE );
561             }
562             if ( indentation != null ) {
563                 PhyloXmlNodeWriter.toPhyloXml( getWriter(), node, getPhyloXmlLevel(), indentation.toString() );
564             }
565             else {
566                 PhyloXmlNodeWriter.toPhyloXml( getWriter(), node, getPhyloXmlLevel(), "" );
567             }
568             if ( node.isExternal() ) {
569                 getWriter().write( ForesterUtil.LINE_SEPARATOR );
570                 if ( indentation != null ) {
571                     getWriter().write( indentation.toString() );
572                 }
573                 PhylogenyDataUtil.appendClose( getWriter(), PhyloXmlMapping.CLADE );
574             }
575         }
576         else if ( getOutputFormt() == FORMAT.NHX ) {
577             getBuffer().append( node.toNewHampshireX() );
578         }
579         else if ( getOutputFormt() == FORMAT.NH ) {
580             getBuffer().append( node.toNewHampshire( isSimpleNH(),
581                                                      isWriteDistanceToParentInNH(),
582                                                      isWriteConfidenceValuesInBracketsInNH() ) );
583         }
584     }
585
586     private boolean isWriteConfidenceValuesInBracketsInNH() {
587         return _write_conf_values_in_branckets_in_nh;
588     }
589
590     private void setWriteConfidenceValuesInBracketsInNH( final boolean write_conf_values_in_branckets_in_nh ) {
591         _write_conf_values_in_branckets_in_nh = write_conf_values_in_branckets_in_nh;
592     }
593
594     private void writeOpenClade( final PhylogenyNode node ) throws IOException {
595         if ( !isSawComma() ) {
596             if ( !node.isRoot() && node.isFirstChildNode() ) {
597                 increaseNodeLevel();
598             }
599             if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
600                 getWriter().write( ForesterUtil.LINE_SEPARATOR );
601                 if ( isIndentPhyloxml() ) {
602                     getWriter().write( createIndentation().toString() );
603                 }
604                 PhylogenyDataUtil.appendOpen( getWriter(), PhyloXmlMapping.CLADE );
605             }
606             else if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
607                 getBuffer().append( "(" );
608             }
609         }
610         setSawComma( false );
611     }
612
613     private void writeOutput( final Writer writer, final Phylogeny tree ) throws IOException {
614         if ( getOutputFormt() != FORMAT.PHYLO_XML ) {
615             throw new RuntimeException( "method inappropriately called" );
616         }
617         if ( tree != null ) {
618             reset( writer, tree );
619             boolean rerootable = true;
620             String unit = "";
621             String type = "";
622             String rooted = "false";
623             if ( tree.isRooted() ) {
624                 rooted = "true";
625             }
626             if ( !tree.isRerootable() ) {
627                 rerootable = false;
628             }
629             if ( !ForesterUtil.isEmpty( tree.getDistanceUnit() ) ) {
630                 unit = tree.getDistanceUnit();
631             }
632             if ( !ForesterUtil.isEmpty( tree.getType() ) ) {
633                 type = tree.getType();
634             }
635             if ( rerootable ) {
636                 PhylogenyDataUtil.appendOpen( writer,
637                                               PhyloXmlMapping.PHYLOGENY,
638                                               PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
639                                               rooted,
640                                               PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
641                                               unit,
642                                               PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
643                                               type );
644             }
645             else {
646                 PhylogenyDataUtil.appendOpen( writer,
647                                               PhyloXmlMapping.PHYLOGENY,
648                                               PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
649                                               rooted,
650                                               PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
651                                               unit,
652                                               PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
653                                               type,
654                                               PhyloXmlMapping.PHYLOGENY_IS_REROOTABLE_ATTR,
655                                               "false" );
656             }
657             appendPhylogenyLevelPhyloXml( writer, tree );
658             while ( isHasNext() ) {
659                 next();
660             }
661             writer.write( ForesterUtil.LINE_SEPARATOR );
662             PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.PHYLOGENY );
663         }
664     }
665
666     private void writeToFile( final StringBuffer sb, final File out_file ) throws IOException {
667         if ( out_file.exists() ) {
668             throw new IOException( "attempt to overwrite existing file \"" + out_file.getAbsolutePath() + "\"" );
669         }
670         final PrintWriter out = new PrintWriter( new FileWriter( out_file ), true );
671         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
672             out.print( PHYLO_XML_VERSION_ENCODING_LINE );
673             out.print( ForesterUtil.LINE_SEPARATOR );
674             out.print( PHYLO_XML_NAMESPACE_LINE );
675             out.print( ForesterUtil.LINE_SEPARATOR );
676         }
677         out.print( sb );
678         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
679             out.print( ForesterUtil.LINE_SEPARATOR );
680             out.print( PHYLO_XML_END );
681         }
682         out.flush();
683         out.close();
684     }
685
686     public static PhylogenyWriter createPhylogenyWriter() {
687         return new PhylogenyWriter();
688     }
689
690     private static void writeNexusStart( final Writer writer ) throws IOException {
691         writer.write( NexusConstants.NEXUS );
692         writer.write( ForesterUtil.LINE_SEPARATOR );
693     }
694
695     public static void writeNexusTaxaBlock( final Writer writer, final Phylogeny tree ) throws IOException {
696         writer.write( NexusConstants.BEGIN_TAXA );
697         writer.write( ForesterUtil.LINE_SEPARATOR );
698         writer.write( " " );
699         writer.write( NexusConstants.DIMENSIONS );
700         writer.write( " " );
701         writer.write( NexusConstants.NTAX );
702         writer.write( "=" );
703         writer.write( String.valueOf( tree.getNumberOfExternalNodes() ) );
704         writer.write( ";" );
705         writer.write( ForesterUtil.LINE_SEPARATOR );
706         writer.write( " " );
707         writer.write( NexusConstants.TAXLABELS );
708         for( final PhylogenyNodeIterator it = tree.iteratorExternalForward(); it.hasNext(); ) {
709             final PhylogenyNode node = it.next();
710             writer.write( " " );
711             String data = "";
712             if ( !ForesterUtil.isEmpty( node.getName() ) ) {
713                 data = node.getName();
714             }
715             else if ( node.getNodeData().isHasTaxonomy() ) {
716                 if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
717                     data = node.getNodeData().getTaxonomy().getTaxonomyCode();
718                 }
719                 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
720                     data = node.getNodeData().getTaxonomy().getScientificName();
721                 }
722                 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) {
723                     data = node.getNodeData().getTaxonomy().getCommonName();
724                 }
725                 else if ( node.getNodeData().getTaxonomy().getTaxonomyCode() != null ) {
726                     data = node.getNodeData().getTaxonomy().getTaxonomyCode();
727                 }
728             }
729             else if ( node.getNodeData().isHasSequence() ) {
730                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
731                     data = node.getNodeData().getSequence().getName();
732                 }
733             }
734             if ( data.length() > 0 ) {
735                 data = data.replaceAll( " ", "_" );
736             }
737             writer.write( data );
738         }
739         writer.write( ";" );
740         writer.write( ForesterUtil.LINE_SEPARATOR );
741         writer.write( NexusConstants.END );
742         writer.write( ForesterUtil.LINE_SEPARATOR );
743     }
744
745     public static void writeNexusTreesBlock( final Writer writer, final List<Phylogeny> trees ) throws IOException {
746         writer.write( NexusConstants.BEGIN_TREES );
747         writer.write( ForesterUtil.LINE_SEPARATOR );
748         int i = 1;
749         for( final Phylogeny phylogeny : trees ) {
750             writer.write( " " );
751             writer.write( NexusConstants.TREE );
752             writer.write( " " );
753             if ( !ForesterUtil.isEmpty( phylogeny.getName() ) ) {
754                 writer.write( "\'" );
755                 writer.write( phylogeny.getName() );
756                 writer.write( "\'" );
757             }
758             else {
759                 writer.write( "tree" );
760                 writer.write( String.valueOf( i ) );
761             }
762             writer.write( "=" );
763             if ( phylogeny.isRooted() ) {
764                 writer.write( "[&R]" );
765             }
766             else {
767                 writer.write( "[&U]" );
768             }
769             writer.write( phylogeny.toNewHampshire( false ) );
770             writer.write( ForesterUtil.LINE_SEPARATOR );
771             i++;
772         }
773         writer.write( NexusConstants.END );
774         writer.write( ForesterUtil.LINE_SEPARATOR );
775     }
776
777     private static void writePhyloXmlEnd( final Writer writer ) throws IOException {
778         writer.write( ForesterUtil.LINE_SEPARATOR );
779         writer.write( PhylogenyWriter.PHYLO_XML_END );
780     }
781
782     private static void writePhyloXmlStart( final Writer writer ) throws IOException {
783         writer.write( PhylogenyWriter.PHYLO_XML_VERSION_ENCODING_LINE );
784         writer.write( ForesterUtil.LINE_SEPARATOR );
785         writer.write( PhylogenyWriter.PHYLO_XML_NAMESPACE_LINE );
786         writer.write( ForesterUtil.LINE_SEPARATOR );
787     }
788
789     public static enum FORMAT {
790         NH, NHX, PHYLO_XML, NEXUS;
791     }
792 }