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