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