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