inprogress
[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: https://sites.google.com/site/cmzmasek/home/software/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.PhylogenyNode.NH_CONVERSION_SUPPORT_VALUE_STYLE;
45 import org.forester.phylogeny.data.PhylogenyDataUtil;
46 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
47 import org.forester.phylogeny.iterators.PostOrderStackObject;
48 import org.forester.util.ForesterConstants;
49 import org.forester.util.ForesterUtil;
50
51 public final class PhylogenyWriter {
52
53     public final static boolean         INDENT_PHYLOXML_DEAFULT         = true;
54     public final static String          PHYLO_XML_INTENDATION_BASE      = "  ";
55     public final static String          PHYLO_XML_VERSION_ENCODING_LINE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
56     public final static String          PHYLO_XML_NAMESPACE_LINE        = "<phyloxml xmlns:xsi=\""
57                                                                                 + ForesterConstants.XML_SCHEMA_INSTANCE
58                                                                                 + "\" xsi:schemaLocation=\""
59                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
60                                                                                 + " "
61                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
62                                                                                 + "/"
63                                                                                 + ForesterConstants.PHYLO_XML_VERSION
64                                                                                 + "/" + ForesterConstants.PHYLO_XML_XSD
65                                                                                 + "\" " + "xmlns=\""
66                                                                                 + ForesterConstants.PHYLO_XML_LOCATION
67                                                                                 + "\">";
68     public final static String          PHYLO_XML_END                   = "</phyloxml>";
69     private boolean                     _saw_comma;
70     private StringBuffer                _buffer;
71     private Writer                      _writer;
72     private PhylogenyNode               _root;
73     private boolean                     _has_next;
74     private Stack<PostOrderStackObject> _stack;
75     private boolean                     _simple_nh;
76     private boolean                     _nh_write_distance_to_parent;
77     NH_CONVERSION_SUPPORT_VALUE_STYLE   _nh_conversion_support_style;
78     private boolean                     _indent_phyloxml;
79     private int                         _node_level;
80     private int                         _phyloxml_level;
81     private FORMAT                      _format;
82
83     public PhylogenyWriter() {
84         setIndentPhyloxml( INDENT_PHYLOXML_DEAFULT );
85         setNhConversionSupportStyle( NH_CONVERSION_SUPPORT_VALUE_STYLE.NONE );
86     }
87
88     private void appendPhylogenyLevelPhyloXml( final Writer writer, final Phylogeny tree ) throws IOException {
89         final String indentation = new String();
90         if ( !ForesterUtil.isEmpty( tree.getName() ) ) {
91             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.PHYLOGENY_NAME, tree.getName(), indentation );
92         }
93         if ( tree.getIdentifier() != null ) {
94             if ( ForesterUtil.isEmpty( tree.getIdentifier().getProvider() ) ) {
95                 PhylogenyDataUtil.appendElement( writer,
96                                                  PhyloXmlMapping.IDENTIFIER,
97                                                  tree.getIdentifier().getValue(),
98                                                  indentation );
99             }
100             PhylogenyDataUtil.appendElement( writer,
101                                              PhyloXmlMapping.IDENTIFIER,
102                                              tree.getIdentifier().getValue(),
103                                              PhyloXmlMapping.IDENTIFIER_PROVIDER_ATTR,
104                                              tree.getIdentifier().getProvider(),
105                                              indentation );
106         }
107         if ( !ForesterUtil.isEmpty( tree.getDescription() ) ) {
108             PhylogenyDataUtil.appendElement( writer,
109                                              PhyloXmlMapping.PHYLOGENY_DESCRIPTION,
110                                              tree.getDescription(),
111                                              indentation );
112         }
113         if ( tree.getConfidence() != null ) {
114             if ( ForesterUtil.isEmpty( tree.getConfidence().getType() ) ) {
115                 PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.CONFIDENCE, tree.getConfidence().getValue()
116                         + "", indentation );
117             }
118             PhylogenyDataUtil.appendElement( writer,
119                                              PhyloXmlMapping.CONFIDENCE,
120                                              tree.getConfidence().getValue() + "",
121                                              PhyloXmlMapping.CONFIDENCE_TYPE_ATTR,
122                                              tree.getConfidence().getType(),
123                                              indentation );
124         }
125     }
126
127     private StringBuffer createIndentation() {
128         if ( !isIndentPhyloxml() ) {
129             return null;
130         }
131         final StringBuffer sb = new StringBuffer( getNodeLevel() * 2 );
132         for( int i = 0; i < getNodeLevel(); ++i ) {
133             sb.append( PhylogenyWriter.PHYLO_XML_INTENDATION_BASE );
134         }
135         return sb;
136     }
137
138     private void decreaseNodeLevel() {
139         --_node_level;
140     }
141
142     private StringBuffer getBuffer() {
143         return _buffer;
144     }
145
146     private int getNodeLevel() {
147         return _node_level;
148     }
149
150     private StringBuffer getOutput( final Phylogeny tree ) throws IOException {
151         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
152             throw new RuntimeException( "method inappropriately called" );
153         }
154         if ( tree != null ) {
155             reset( tree );
156             while ( isHasNext() ) {
157                 next();
158             }
159             if ( getOutputFormt() == FORMAT.NH ) {
160                 getBuffer().append( ';' );
161             }
162             return getBuffer();
163         }
164         else {
165             return new StringBuffer( 0 );
166         }
167     }
168
169     private FORMAT getOutputFormt() {
170         return _format;
171     }
172
173     private int getPhyloXmlLevel() {
174         return _phyloxml_level;
175     }
176
177     private PhylogenyNode getRoot() {
178         return _root;
179     }
180
181     private Stack<PostOrderStackObject> getStack() {
182         return _stack;
183     }
184
185     private Writer getWriter() {
186         return _writer;
187     }
188
189     private void increaseNodeLevel() {
190         ++_node_level;
191     }
192
193     private boolean isHasNext() {
194         return _has_next;
195     }
196
197     private boolean isIndentPhyloxml() {
198         return _indent_phyloxml;
199     }
200
201     private boolean isSawComma() {
202         return _saw_comma;
203     }
204
205     private boolean isSimpleNH() {
206         return _simple_nh;
207     }
208
209     private boolean isWriteDistanceToParentInNH() {
210         return _nh_write_distance_to_parent;
211     }
212
213     private void next() throws IOException {
214         while ( true ) {
215             final PostOrderStackObject si = getStack().pop();
216             final PhylogenyNode node = si.getNode();
217             final int phase = si.getPhase();
218             if ( phase > node.getNumberOfDescendants() ) {
219                 setHasNext( node != getRoot() );
220                 if ( ( getOutputFormt() != FORMAT.PHYLO_XML ) || node.isExternal() ) {
221                     if ( !node.isRoot() && node.isFirstChildNode() ) {
222                         increaseNodeLevel();
223                     }
224                     if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
225                         writeNode( node, createIndentation() );
226                     }
227                     else {
228                         writeNode( node, null );
229                     }
230                 }
231                 if ( !node.isRoot() ) {
232                     if ( !node.isLastChildNode() ) {
233                         writeCladeSeparator();
234                     }
235                     else {
236                         writeCloseClade();
237                     }
238                 }
239                 return;
240             }
241             else {
242                 getStack().push( new PostOrderStackObject( node, ( phase + 1 ) ) );
243                 if ( node.isInternal() ) {
244                     getStack().push( new PostOrderStackObject( node.getChildNode( phase - 1 ), 1 ) );
245                     writeOpenClade( node );
246                     if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
247                         if ( phase == 1 ) {
248                             writeNode( node, createIndentation() );
249                         }
250                     }
251                 }
252             }
253         }
254     }
255
256     private void reset( final Phylogeny tree ) {
257         setBuffer( new StringBuffer() );
258         setWriter( null );
259         setSawComma( false );
260         setHasNext( true );
261         setRoot( tree.getRoot() );
262         setStack( new Stack<PostOrderStackObject>() );
263         getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
264         setNodeLevel( 1 );
265     }
266
267     private void reset( final Writer writer, final Phylogeny tree ) {
268         setBuffer( null );
269         setWriter( writer );
270         setSawComma( false );
271         setHasNext( true );
272         setRoot( tree.getRoot() );
273         setStack( new Stack<PostOrderStackObject>() );
274         getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
275         setNodeLevel( 1 );
276     }
277
278     private void setBuffer( final StringBuffer buffer ) {
279         _buffer = buffer;
280     }
281
282     private void setHasNext( final boolean has_next ) {
283         _has_next = has_next;
284     }
285
286     public void setIndentPhyloxml( final boolean indent_phyloxml ) {
287         _indent_phyloxml = indent_phyloxml;
288     }
289
290     private void setNodeLevel( final int level ) {
291         _node_level = level;
292     }
293
294     private void setOutputFormt( final FORMAT format ) {
295         _format = format;
296     }
297
298     private void setPhyloXmlLevel( final int phyloxml_level ) {
299         _phyloxml_level = phyloxml_level;
300     }
301
302     private void setRoot( final PhylogenyNode root ) {
303         _root = root;
304     }
305
306     private void setSawComma( final boolean saw_comma ) {
307         _saw_comma = saw_comma;
308     }
309
310     private void setSimpleNH( final boolean simple_nh ) {
311         _simple_nh = simple_nh;
312     }
313
314     private void setStack( final Stack<PostOrderStackObject> stack ) {
315         _stack = stack;
316     }
317
318     private void setWriteDistanceToParentInNH( final boolean nh_write_distance_to_parent ) {
319         _nh_write_distance_to_parent = nh_write_distance_to_parent;
320     }
321
322     private void setWriter( final Writer writer ) {
323         _writer = writer;
324     }
325
326     public void toNewHampshire( final List<Phylogeny> trees,
327                                 final boolean simple_nh,
328                                 final boolean write_distance_to_parent,
329                                 final File out_file,
330                                 final String separator ) throws IOException {
331         final Iterator<Phylogeny> it = trees.iterator();
332         final StringBuffer sb = new StringBuffer();
333         while ( it.hasNext() ) {
334             sb.append( toNewHampshire( it.next(), simple_nh, write_distance_to_parent ) );
335             sb.append( separator );
336         }
337         writeToFile( sb, out_file );
338     }
339
340     public StringBuffer toNewHampshire( final Phylogeny tree,
341                                         final boolean simple_nh,
342                                         final boolean nh_write_distance_to_parent,
343                                         final NH_CONVERSION_SUPPORT_VALUE_STYLE svs ) throws IOException {
344         setOutputFormt( FORMAT.NH );
345         setNhConversionSupportStyle( svs );
346         setSimpleNH( simple_nh );
347         setWriteDistanceToParentInNH( nh_write_distance_to_parent );
348         return getOutput( tree );
349     }
350
351     public StringBuffer toNewHampshire( final Phylogeny tree,
352                                         final boolean simple_nh,
353                                         final boolean nh_write_distance_to_parent ) throws IOException {
354         setOutputFormt( FORMAT.NH );
355         setSimpleNH( simple_nh );
356         setWriteDistanceToParentInNH( nh_write_distance_to_parent );
357         return getOutput( tree );
358     }
359
360     public void toNewHampshire( final Phylogeny tree,
361                                 final boolean simple_nh,
362                                 final boolean write_distance_to_parent,
363                                 final File out_file ) throws IOException {
364         writeToFile( toNewHampshire( tree, simple_nh, write_distance_to_parent ), out_file );
365     }
366
367     public void toNewHampshire( final Phylogeny tree,
368                                 final boolean simple_nh,
369                                 final boolean write_distance_to_parent,
370                                 final NH_CONVERSION_SUPPORT_VALUE_STYLE svs,
371                                 final File out_file ) throws IOException {
372         writeToFile( toNewHampshire( tree, simple_nh, write_distance_to_parent, svs ), out_file );
373     }
374
375     public void toNewHampshire( final Phylogeny[] trees,
376                                 final boolean simple_nh,
377                                 final boolean write_distance_to_parent,
378                                 final File out_file,
379                                 final String separator ) throws IOException {
380         final StringBuffer sb = new StringBuffer();
381         for( final Phylogeny element : trees ) {
382             sb.append( toNewHampshire( element, simple_nh, write_distance_to_parent ) );
383             sb.append( separator );
384         }
385         writeToFile( sb, out_file );
386     }
387
388     public void toNewHampshireX( final List<Phylogeny> trees, final File out_file, final String separator )
389             throws IOException {
390         final Iterator<Phylogeny> it = trees.iterator();
391         final StringBuffer sb = new StringBuffer();
392         while ( it.hasNext() ) {
393             sb.append( toNewHampshireX( it.next() ) );
394             sb.append( separator );
395         }
396         writeToFile( sb, out_file );
397     }
398
399     public StringBuffer toNewHampshireX( final Phylogeny tree ) throws IOException {
400         setOutputFormt( FORMAT.NHX );
401         return getOutput( tree );
402     }
403
404     public void toNewHampshireX( final Phylogeny tree, final File out_file ) throws IOException {
405         writeToFile( toNewHampshireX( tree ), out_file );
406     }
407
408     public void toNewHampshireX( final Phylogeny[] trees, final File out_file, final String separator )
409             throws IOException {
410         final StringBuffer sb = new StringBuffer();
411         for( final Phylogeny element : trees ) {
412             sb.append( toNewHampshireX( element ) );
413             sb.append( separator );
414         }
415         writeToFile( sb, out_file );
416     }
417
418     public void toNexus( final File out_file, final Phylogeny tree, final NH_CONVERSION_SUPPORT_VALUE_STYLE svs )
419             throws IOException {
420         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
421         final List<Phylogeny> trees = new ArrayList<Phylogeny>( 1 );
422         trees.add( tree );
423         writeNexusStart( writer );
424         writeNexusTaxaBlock( writer, tree );
425         writeNexusTreesBlock( writer, trees, svs );
426         writer.flush();
427         writer.close();
428     }
429
430     public StringBuffer toNexus( final Phylogeny tree, final NH_CONVERSION_SUPPORT_VALUE_STYLE svs ) throws IOException {
431         final StringWriter string_writer = new StringWriter();
432         final Writer writer = new BufferedWriter( string_writer );
433         final List<Phylogeny> trees = new ArrayList<Phylogeny>( 1 );
434         trees.add( tree );
435         writeNexusStart( writer );
436         writeNexusTaxaBlock( writer, tree );
437         writeNexusTreesBlock( writer, trees, svs );
438         writer.flush();
439         writer.close();
440         return string_writer.getBuffer();
441     }
442
443     public void toPhyloXML( final File out_file,
444                             final List<Phylogeny> trees,
445                             final int phyloxml_level,
446                             final String separator ) throws IOException {
447         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
448         toPhyloXML( writer, trees, phyloxml_level, separator );
449         writer.flush();
450         writer.close();
451     }
452
453     public void toPhyloXML( final File out_file, final Phylogeny tree, final int phyloxml_level ) throws IOException {
454         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
455         writePhyloXmlStart( writer );
456         toPhyloXMLNoPhyloXmlSource( writer, tree, phyloxml_level );
457         writePhyloXmlEnd( writer );
458         writer.flush();
459         writer.close();
460     }
461
462     public StringBuffer toPhyloXML( final Phylogeny tree, final int phyloxml_level ) throws IOException {
463         final StringWriter string_writer = new StringWriter();
464         final Writer writer = new BufferedWriter( string_writer );
465         setPhyloXmlLevel( phyloxml_level );
466         setOutputFormt( FORMAT.PHYLO_XML );
467         writePhyloXmlStart( writer );
468         writeOutput( writer, tree );
469         writePhyloXmlEnd( writer );
470         writer.flush();
471         writer.close();
472         return string_writer.getBuffer();
473     }
474
475     public void toPhyloXML( final Phylogeny[] trees,
476                             final int phyloxml_level,
477                             final File out_file,
478                             final String separator ) throws IOException {
479         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
480         toPhyloXML( writer, trees, phyloxml_level, separator );
481         writer.flush();
482         writer.close();
483     }
484
485     public void toPhyloXML( final Phylogeny phy, final int phyloxml_level, final File out_file ) throws IOException {
486         final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
487         toPhyloXML( writer, phy, phyloxml_level );
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             PhyloXmlNodeWriter.toPhyloXml( getWriter(),
563                                            node,
564                                            getPhyloXmlLevel(),
565                                            indentation != null ? indentation.toString() : "" );
566             if ( node.isExternal() ) {
567                 getWriter().write( ForesterUtil.LINE_SEPARATOR );
568                 if ( indentation != null ) {
569                     getWriter().write( indentation.toString() );
570                 }
571                 PhylogenyDataUtil.appendClose( getWriter(), PhyloXmlMapping.CLADE );
572             }
573         }
574         else if ( getOutputFormt() == FORMAT.NHX ) {
575             getBuffer().append( node.toNewHampshireX() );
576         }
577         else if ( getOutputFormt() == FORMAT.NH ) {
578             getBuffer().append( node.toNewHampshire( isSimpleNH(),
579                                                      isWriteDistanceToParentInNH(),
580                                                      getNhConversionSupportStyle() ) );
581         }
582     }
583
584     private NH_CONVERSION_SUPPORT_VALUE_STYLE getNhConversionSupportStyle() {
585         return _nh_conversion_support_style;
586     }
587
588     private void setNhConversionSupportStyle( final NH_CONVERSION_SUPPORT_VALUE_STYLE nh_conversion_support_style ) {
589         _nh_conversion_support_style = nh_conversion_support_style;
590     }
591
592     private void writeOpenClade( final PhylogenyNode node ) throws IOException {
593         if ( !isSawComma() ) {
594             if ( !node.isRoot() && node.isFirstChildNode() ) {
595                 increaseNodeLevel();
596             }
597             if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
598                 getWriter().write( ForesterUtil.LINE_SEPARATOR );
599                 if ( isIndentPhyloxml() ) {
600                     getWriter().write( createIndentation().toString() );
601                 }
602                 if ( node.isCollapse() ) {
603                     PhylogenyDataUtil.appendOpen( getWriter(),
604                                                   PhyloXmlMapping.CLADE,
605                                                   PhyloXmlMapping.NODE_COLLAPSE,
606                                                   "true" );
607                 }
608                 else {
609                     PhylogenyDataUtil.appendOpen( getWriter(), PhyloXmlMapping.CLADE );
610                 }
611             }
612             else if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
613                 getBuffer().append( "(" );
614             }
615         }
616         setSawComma( false );
617     }
618
619     private void writeOutput( final Writer writer, final Phylogeny tree ) throws IOException {
620         if ( getOutputFormt() != FORMAT.PHYLO_XML ) {
621             throw new RuntimeException( "method inappropriately called" );
622         }
623         if ( tree != null ) {
624             reset( writer, tree );
625             boolean rerootable = true;
626             String unit = "";
627             String type = "";
628             String rooted = "false";
629             if ( tree.isRooted() ) {
630                 rooted = "true";
631             }
632             if ( !tree.isRerootable() ) {
633                 rerootable = false;
634             }
635             if ( !ForesterUtil.isEmpty( tree.getDistanceUnit() ) ) {
636                 unit = tree.getDistanceUnit();
637             }
638             if ( !ForesterUtil.isEmpty( tree.getType() ) ) {
639                 type = tree.getType();
640             }
641             if ( rerootable ) {
642                 PhylogenyDataUtil.appendOpen( writer,
643                                               PhyloXmlMapping.PHYLOGENY,
644                                               PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
645                                               rooted,
646                                               PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
647                                               unit,
648                                               PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
649                                               type );
650             }
651             else {
652                 PhylogenyDataUtil.appendOpen( writer,
653                                               PhyloXmlMapping.PHYLOGENY,
654                                               PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
655                                               rooted,
656                                               PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
657                                               unit,
658                                               PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
659                                               type,
660                                               PhyloXmlMapping.PHYLOGENY_IS_REROOTABLE_ATTR,
661                                               "false" );
662             }
663             appendPhylogenyLevelPhyloXml( writer, tree );
664             while ( isHasNext() ) {
665                 next();
666             }
667             writer.write( ForesterUtil.LINE_SEPARATOR );
668             PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.PHYLOGENY );
669         }
670     }
671
672     private void writeToFile( final StringBuffer sb, final File out_file ) throws IOException {
673         if ( out_file.exists() ) {
674             throw new IOException( "attempt to overwrite existing file \"" + out_file.getAbsolutePath() + "\"" );
675         }
676         final PrintWriter out = new PrintWriter( new FileWriter( out_file ), true );
677         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
678             out.print( PHYLO_XML_VERSION_ENCODING_LINE );
679             out.print( ForesterUtil.LINE_SEPARATOR );
680             out.print( PHYLO_XML_NAMESPACE_LINE );
681             out.print( ForesterUtil.LINE_SEPARATOR );
682         }
683         out.print( sb );
684         if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
685             out.print( ForesterUtil.LINE_SEPARATOR );
686             out.print( PHYLO_XML_END );
687         }
688         out.flush();
689         out.close();
690     }
691
692     public static PhylogenyWriter createPhylogenyWriter() {
693         return new PhylogenyWriter();
694     }
695
696     private static void writeNexusStart( final Writer writer ) throws IOException {
697         writer.write( NexusConstants.NEXUS );
698         writer.write( ForesterUtil.LINE_SEPARATOR );
699     }
700
701     public static void writeNexusTaxaBlock( final Writer writer, final Phylogeny tree ) throws IOException {
702         writer.write( NexusConstants.BEGIN_TAXA );
703         writer.write( ForesterUtil.LINE_SEPARATOR );
704         writer.write( " " );
705         writer.write( NexusConstants.DIMENSIONS );
706         writer.write( " " );
707         writer.write( NexusConstants.NTAX );
708         writer.write( "=" );
709         writer.write( String.valueOf( tree.getNumberOfExternalNodes() ) );
710         writer.write( ";" );
711         writer.write( ForesterUtil.LINE_SEPARATOR );
712         writer.write( " " );
713         writer.write( NexusConstants.TAXLABELS );
714         for( final PhylogenyNodeIterator it = tree.iteratorExternalForward(); it.hasNext(); ) {
715             final PhylogenyNode node = it.next();
716             writer.write( " " );
717             String data = "";
718             if ( !ForesterUtil.isEmpty( node.getName() ) ) {
719                 data = node.getName();
720             }
721             else if ( node.getNodeData().isHasTaxonomy() ) {
722                 if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
723                     data = node.getNodeData().getTaxonomy().getTaxonomyCode();
724                 }
725                 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
726                     data = node.getNodeData().getTaxonomy().getScientificName();
727                 }
728                 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) {
729                     data = node.getNodeData().getTaxonomy().getCommonName();
730                 }
731                 else if ( node.getNodeData().getTaxonomy().getTaxonomyCode() != null ) {
732                     data = node.getNodeData().getTaxonomy().getTaxonomyCode();
733                 }
734             }
735             else if ( node.getNodeData().isHasSequence() ) {
736                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
737                     data = node.getNodeData().getSequence().getName();
738                 }
739             }
740             if ( data.length() > 0 ) {
741                 data = data.replaceAll( " ", "_" );
742             }
743             writer.write( data );
744         }
745         writer.write( ";" );
746         writer.write( ForesterUtil.LINE_SEPARATOR );
747         writer.write( NexusConstants.END );
748         writer.write( ForesterUtil.LINE_SEPARATOR );
749     }
750
751     public static void writeNexusTreesBlock( final Writer writer,
752                                              final List<Phylogeny> trees,
753                                              final NH_CONVERSION_SUPPORT_VALUE_STYLE svs ) throws IOException {
754         writer.write( NexusConstants.BEGIN_TREES );
755         writer.write( ForesterUtil.LINE_SEPARATOR );
756         int i = 1;
757         for( final Phylogeny phylogeny : trees ) {
758             writer.write( " " );
759             writer.write( NexusConstants.TREE );
760             writer.write( " " );
761             if ( !ForesterUtil.isEmpty( phylogeny.getName() ) ) {
762                 writer.write( "\'" );
763                 writer.write( phylogeny.getName() );
764                 writer.write( "\'" );
765             }
766             else {
767                 writer.write( "tree" );
768                 writer.write( String.valueOf( i ) );
769             }
770             writer.write( "=" );
771             if ( phylogeny.isRooted() ) {
772                 writer.write( "[&R]" );
773             }
774             else {
775                 writer.write( "[&U]" );
776             }
777             writer.write( phylogeny.toNewHampshire( false, svs ) );
778             writer.write( ForesterUtil.LINE_SEPARATOR );
779             i++;
780         }
781         writer.write( NexusConstants.END );
782         writer.write( ForesterUtil.LINE_SEPARATOR );
783     }
784
785     private static void writePhyloXmlEnd( final Writer writer ) throws IOException {
786         writer.write( ForesterUtil.LINE_SEPARATOR );
787         writer.write( PhylogenyWriter.PHYLO_XML_END );
788     }
789
790     private static void writePhyloXmlStart( final Writer writer ) throws IOException {
791         writer.write( PhylogenyWriter.PHYLO_XML_VERSION_ENCODING_LINE );
792         writer.write( ForesterUtil.LINE_SEPARATOR );
793         writer.write( PhylogenyWriter.PHYLO_XML_NAMESPACE_LINE );
794         writer.write( ForesterUtil.LINE_SEPARATOR );
795     }
796
797     public static enum FORMAT {
798         NH, NHX, PHYLO_XML, NEXUS;
799     }
800 }