2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
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.
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.
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
23 // Contact: phylosoft @ gmail . com
24 // WWW: www.phylosoft.org/forester
26 package org.forester.io.writers;
28 import java.io.BufferedWriter;
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;
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;
50 public final class PhylogenyWriter {
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
60 + ForesterConstants.PHYLO_XML_LOCATION
62 + ForesterConstants.PHYLO_XML_VERSION
63 + "/" + ForesterConstants.PHYLO_XML_XSD
65 + ForesterConstants.PHYLO_XML_LOCATION
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;
81 public PhylogenyWriter() {
82 setIndentPhyloxml( INDENT_PHYLOXML_DEAFULT );
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 );
90 if ( tree.getIdentifier() != null ) {
91 if ( ForesterUtil.isEmpty( tree.getIdentifier().getProvider() ) ) {
92 PhylogenyDataUtil.appendElement( writer,
93 PhyloXmlMapping.IDENTIFIER,
94 tree.getIdentifier().getValue(),
97 PhylogenyDataUtil.appendElement( writer,
98 PhyloXmlMapping.IDENTIFIER,
99 tree.getIdentifier().getValue(),
100 PhyloXmlMapping.IDENTIFIER_PROVIDER_ATTR,
101 tree.getIdentifier().getProvider(),
104 if ( !ForesterUtil.isEmpty( tree.getDescription() ) ) {
105 PhylogenyDataUtil.appendElement( writer,
106 PhyloXmlMapping.PHYLOGENY_DESCRIPTION,
107 tree.getDescription(),
110 if ( tree.getConfidence() != null ) {
111 if ( ForesterUtil.isEmpty( tree.getConfidence().getType() ) ) {
112 PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.CONFIDENCE, tree.getConfidence().getValue()
115 PhylogenyDataUtil.appendElement( writer,
116 PhyloXmlMapping.CONFIDENCE,
117 tree.getConfidence().getValue() + "",
118 PhyloXmlMapping.CONFIDENCE_TYPE_ATTR,
119 tree.getConfidence().getType(),
124 private StringBuffer createIndentation() {
125 if ( !isIndentPhyloxml() ) {
128 final StringBuffer sb = new StringBuffer( getNodeLevel() * 2 );
129 for( int i = 0; i < getNodeLevel(); ++i ) {
130 sb.append( PhylogenyWriter.PHYLO_XML_INTENDATION_BASE );
135 private void decreaseNodeLevel() {
139 private StringBuffer getBuffer() {
143 private int getNodeLevel() {
147 private StringBuffer getOutput( final Phylogeny tree ) throws IOException {
148 if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
149 throw new RuntimeException( "method inappropriately called" );
151 if ( tree != null ) {
153 while ( isHasNext() ) {
156 if ( getOutputFormt() == FORMAT.NH ) {
157 getBuffer().append( ';' );
162 return new StringBuffer( 0 );
166 private FORMAT getOutputFormt() {
170 private int getPhyloXmlLevel() {
171 return _phyloxml_level;
174 private PhylogenyNode getRoot() {
178 private Stack<PostOrderStackObject> getStack() {
182 private Writer getWriter() {
186 private void increaseNodeLevel() {
190 private boolean isHasNext() {
194 private boolean isIndentPhyloxml() {
195 return _indent_phyloxml;
198 private boolean isSawComma() {
202 private boolean isSimpleNH() {
206 private boolean isWriteDistanceToParentInNH() {
207 return _nh_write_distance_to_parent;
210 private void next() throws IOException {
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() ) {
221 if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
222 writeNode( node, createIndentation() );
225 writeNode( node, null );
228 if ( !node.isRoot() ) {
229 if ( !node.isLastChildNode() ) {
230 writeCladeSeparator();
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 ) {
245 writeNode( node, createIndentation() );
253 private void reset( final Phylogeny tree ) {
254 setBuffer( new StringBuffer() );
256 setSawComma( false );
258 setRoot( tree.getRoot() );
259 setStack( new Stack<PostOrderStackObject>() );
260 getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
264 private void reset( final Writer writer, final Phylogeny tree ) {
267 setSawComma( false );
269 setRoot( tree.getRoot() );
270 setStack( new Stack<PostOrderStackObject>() );
271 getStack().push( new PostOrderStackObject( tree.getRoot(), 1 ) );
275 private void setBuffer( final StringBuffer buffer ) {
279 private void setHasNext( final boolean has_next ) {
280 _has_next = has_next;
283 public void setIndentPhyloxml( final boolean indent_phyloxml ) {
284 _indent_phyloxml = indent_phyloxml;
287 private void setNodeLevel( final int level ) {
291 private void setOutputFormt( final FORMAT format ) {
295 private void setPhyloXmlLevel( final int phyloxml_level ) {
296 _phyloxml_level = phyloxml_level;
299 private void setRoot( final PhylogenyNode root ) {
303 private void setSawComma( final boolean saw_comma ) {
304 _saw_comma = saw_comma;
307 private void setSimpleNH( final boolean simple_nh ) {
308 _simple_nh = simple_nh;
311 private void setStack( final Stack<PostOrderStackObject> stack ) {
315 private void setWriteDistanceToParentInNH( final boolean nh_write_distance_to_parent ) {
316 _nh_write_distance_to_parent = nh_write_distance_to_parent;
319 private void setWriter( final Writer writer ) {
323 public void toNewHampshire( final List<Phylogeny> trees,
324 final boolean simple_nh,
325 final boolean write_distance_to_parent,
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 );
334 writeToFile( sb, out_file );
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 );
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 );
353 public void toNewHampshire( final Phylogeny[] trees,
354 final boolean simple_nh,
355 final boolean write_distance_to_parent,
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 );
363 writeToFile( sb, out_file );
366 public void toNewHampshireX( final List<Phylogeny> trees, final File out_file, final String separator )
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 );
374 writeToFile( sb, out_file );
377 public StringBuffer toNewHampshireX( final Phylogeny tree ) throws IOException {
378 setOutputFormt( FORMAT.NHX );
379 return getOutput( tree );
382 public void toNewHampshireX( final Phylogeny tree, final File out_file ) throws IOException {
383 writeToFile( toNewHampshireX( tree ), out_file );
386 public void toNewHampshireX( final Phylogeny[] trees, final File out_file, final String separator )
388 final StringBuffer sb = new StringBuffer();
389 for( final Phylogeny element : trees ) {
390 sb.append( toNewHampshireX( element ) );
391 sb.append( separator );
393 writeToFile( sb, out_file );
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 );
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 );
409 writeNexusStart( writer );
410 writeNexusTaxaBlock( writer, tree );
411 writeNexusTreesBlock( writer, trees );
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 );
421 writeNexusStart( writer );
422 writeNexusTaxaBlock( writer, tree );
423 writeNexusTreesBlock( writer, trees );
426 return string_writer.getBuffer();
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 );
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 );
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 );
458 return string_writer.getBuffer();
461 public void toPhyloXML( final Phylogeny[] trees,
462 final int phyloxml_level,
464 final String separator ) throws IOException {
465 final Writer writer = new BufferedWriter( new PrintWriter( out_file ) );
466 toPhyloXML( writer, trees, phyloxml_level, separator );
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 );
481 writePhyloXmlEnd( writer );
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 );
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 );
501 writePhyloXmlEnd( writer );
504 private void toPhyloXMLNoPhyloXmlSource( final Writer writer, final Phylogeny tree, final int phyloxml_level )
506 setPhyloXmlLevel( phyloxml_level );
507 setOutputFormt( FORMAT.PHYLO_XML );
508 writeOutput( writer, tree );
511 private void writeCladeSeparator() {
513 if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
514 getBuffer().append( "," );
518 private void writeCloseClade() throws IOException {
520 if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
521 getWriter().write( ForesterUtil.LINE_SEPARATOR );
522 if ( isIndentPhyloxml() ) {
523 getWriter().write( createIndentation().toString() );
525 PhylogenyDataUtil.appendClose( getWriter(), PhyloXmlMapping.CLADE );
527 else if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
528 getBuffer().append( ")" );
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() );
539 PhylogenyDataUtil.appendOpen( getWriter(), PhyloXmlMapping.CLADE );
541 if ( indentation != null ) {
542 PhyloXmlNodeWriter.toPhyloXml( getWriter(), node, getPhyloXmlLevel(), indentation.toString() );
545 PhyloXmlNodeWriter.toPhyloXml( getWriter(), node, getPhyloXmlLevel(), "" );
547 if ( node.isExternal() ) {
548 getWriter().write( ForesterUtil.LINE_SEPARATOR );
549 if ( indentation != null ) {
550 getWriter().write( indentation.toString() );
552 PhylogenyDataUtil.appendClose( getWriter(), PhyloXmlMapping.CLADE );
555 else if ( getOutputFormt() == FORMAT.NHX ) {
556 getBuffer().append( node.toNewHampshireX() );
558 else if ( getOutputFormt() == FORMAT.NH ) {
559 getBuffer().append( node.toNewHampshire( isSimpleNH(), isWriteDistanceToParentInNH() ) );
563 private void writeOpenClade( final PhylogenyNode node ) throws IOException {
564 if ( !isSawComma() ) {
565 if ( !node.isRoot() && node.isFirstChildNode() ) {
568 if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
569 getWriter().write( ForesterUtil.LINE_SEPARATOR );
570 if ( isIndentPhyloxml() ) {
571 getWriter().write( createIndentation().toString() );
573 PhylogenyDataUtil.appendOpen( getWriter(), PhyloXmlMapping.CLADE );
575 else if ( ( getOutputFormt() == FORMAT.NHX ) || ( getOutputFormt() == FORMAT.NH ) ) {
576 getBuffer().append( "(" );
579 setSawComma( false );
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" );
586 if ( tree != null ) {
587 reset( writer, tree );
588 boolean rerootable = true;
591 String rooted = "false";
592 if ( tree.isRooted() ) {
595 if ( !tree.isRerootable() ) {
598 if ( !ForesterUtil.isEmpty( tree.getDistanceUnit() ) ) {
599 unit = tree.getDistanceUnit();
601 if ( !ForesterUtil.isEmpty( tree.getType() ) ) {
602 type = tree.getType();
605 PhylogenyDataUtil.appendOpen( writer,
606 PhyloXmlMapping.PHYLOGENY,
607 PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
609 PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
611 PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
615 PhylogenyDataUtil.appendOpen( writer,
616 PhyloXmlMapping.PHYLOGENY,
617 PhyloXmlMapping.PHYLOGENY_IS_ROOTED_ATTR,
619 PhyloXmlMapping.PHYLOGENY_BRANCHLENGTH_UNIT_ATTR,
621 PhyloXmlMapping.PHYLOGENY_TYPE_ATTR,
623 PhyloXmlMapping.PHYLOGENY_IS_REROOTABLE_ATTR,
626 appendPhylogenyLevelPhyloXml( writer, tree );
627 while ( isHasNext() ) {
630 writer.write( ForesterUtil.LINE_SEPARATOR );
631 PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.PHYLOGENY );
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() + "\"" );
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 );
647 if ( getOutputFormt() == FORMAT.PHYLO_XML ) {
648 out.print( ForesterUtil.LINE_SEPARATOR );
649 out.print( PHYLO_XML_END );
655 public static PhylogenyWriter createPhylogenyWriter() {
656 return new PhylogenyWriter();
659 private static void writeNexusStart( final Writer writer ) throws IOException {
660 writer.write( NexusConstants.NEXUS );
661 writer.write( ForesterUtil.LINE_SEPARATOR );
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 );
668 writer.write( NexusConstants.DIMENSIONS );
670 writer.write( NexusConstants.NTAX );
672 writer.write( String.valueOf( tree.getNumberOfExternalNodes() ) );
674 writer.write( ForesterUtil.LINE_SEPARATOR );
676 writer.write( NexusConstants.TAXLABELS );
677 for( final PhylogenyNodeIterator it = tree.iteratorExternalForward(); it.hasNext(); ) {
678 final PhylogenyNode node = it.next();
681 if ( !ForesterUtil.isEmpty( node.getName() ) ) {
682 data = node.getName();
684 else if ( node.getNodeData().isHasTaxonomy() ) {
685 if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
686 data = node.getNodeData().getTaxonomy().getTaxonomyCode();
688 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
689 data = node.getNodeData().getTaxonomy().getScientificName();
691 else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) {
692 data = node.getNodeData().getTaxonomy().getCommonName();
694 else if ( node.getNodeData().getTaxonomy().getTaxonomyCode() != null ) {
695 data = node.getNodeData().getTaxonomy().getTaxonomyCode();
698 else if ( node.getNodeData().isHasSequence() ) {
699 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
700 data = node.getNodeData().getSequence().getName();
703 if ( data.length() > 0 ) {
704 data = data.replaceAll( " ", "_" );
706 writer.write( data );
709 writer.write( ForesterUtil.LINE_SEPARATOR );
710 writer.write( NexusConstants.END );
711 writer.write( ForesterUtil.LINE_SEPARATOR );
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 );
718 for( final Phylogeny phylogeny : trees ) {
720 writer.write( NexusConstants.TREE );
722 if ( !ForesterUtil.isEmpty( phylogeny.getName() ) ) {
723 writer.write( "\'" );
724 writer.write( phylogeny.getName() );
725 writer.write( "\'" );
728 writer.write( "tree" );
729 writer.write( String.valueOf( i ) );
732 if ( phylogeny.isRooted() ) {
733 writer.write( "[&R]" );
736 writer.write( "[&U]" );
738 writer.write( phylogeny.toNewHampshire( false ) );
739 writer.write( ForesterUtil.LINE_SEPARATOR );
742 writer.write( NexusConstants.END );
743 writer.write( ForesterUtil.LINE_SEPARATOR );
746 private static void writePhyloXmlEnd( final Writer writer ) throws IOException {
747 writer.write( ForesterUtil.LINE_SEPARATOR );
748 writer.write( PhylogenyWriter.PHYLO_XML_END );
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 );
758 public static enum FORMAT {
759 NH, NHX, PHYLO_XML, NEXUS;