Formatting changes
[jalview.git] / src / jalview / io / NewickFile.java
1 /*
2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 */\r
19 \r
20 // NewickFile.java\r
21 // Tree I/O\r
22 // http://evolution.genetics.washington.edu/phylip/newick_doc.html\r
23 package jalview.io;\r
24 \r
25 import jalview.datamodel.*;\r
26 \r
27 import java.io.*;\r
28 \r
29 import java.util.*;\r
30 \r
31 \r
32 /**\r
33  * DOCUMENT ME!\r
34  *\r
35  * @author $author$\r
36  * @version $Revision$\r
37  */\r
38 public class NewickFile extends FileParse\r
39 {\r
40     SequenceNode root;\r
41     private boolean HasBootstrap = false;\r
42     private boolean HasDistances = false;\r
43     private boolean RootHasDistance = false;\r
44 \r
45     // File IO Flags\r
46     boolean ReplaceUnderscores = false;\r
47     boolean printRootInfo = false;\r
48     private com.stevesoft.pat.Regex[] NodeSafeName = new com.stevesoft.pat.Regex[]\r
49         {\r
50             new com.stevesoft.pat.Regex().perlCode("m/[\\[,:'()]/"), // test for requiring quotes\r
51             new com.stevesoft.pat.Regex().perlCode("s/'/''/"), // escaping quote characters\r
52             new com.stevesoft.pat.Regex().perlCode("s/\\/w/_/") // unqoted whitespace transformation\r
53         };\r
54     char QuoteChar = '\'';\r
55 \r
56     /**\r
57      * Creates a new NewickFile object.\r
58      *\r
59      * @param inStr DOCUMENT ME!\r
60      *\r
61      * @throws IOException DOCUMENT ME!\r
62      */\r
63     public NewickFile(String inStr) throws IOException\r
64     {\r
65         super(inStr, "Paste");\r
66     }\r
67 \r
68     /**\r
69      * Creates a new NewickFile object.\r
70      *\r
71      * @param inFile DOCUMENT ME!\r
72      * @param type DOCUMENT ME!\r
73      *\r
74      * @throws IOException DOCUMENT ME!\r
75      */\r
76     public NewickFile(String inFile, String type) throws IOException\r
77     {\r
78         super(inFile, type);\r
79     }\r
80 \r
81     /**\r
82      * Creates a new NewickFile object.\r
83      *\r
84      * @param newtree DOCUMENT ME!\r
85      */\r
86     public NewickFile(SequenceNode newtree)\r
87     {\r
88         root = newtree;\r
89     }\r
90 \r
91     /**\r
92      * Creates a new NewickFile object.\r
93      *\r
94      * @param newtree DOCUMENT ME!\r
95      * @param bootstrap DOCUMENT ME!\r
96      */\r
97     public NewickFile(SequenceNode newtree, boolean bootstrap)\r
98     {\r
99         HasBootstrap = bootstrap;\r
100         root = newtree;\r
101     }\r
102 \r
103     /**\r
104      * Creates a new NewickFile object.\r
105      *\r
106      * @param newtree DOCUMENT ME!\r
107      * @param bootstrap DOCUMENT ME!\r
108      * @param distances DOCUMENT ME!\r
109      */\r
110     public NewickFile(SequenceNode newtree, boolean bootstrap, boolean distances)\r
111     {\r
112         root = newtree;\r
113         HasBootstrap = bootstrap;\r
114         HasDistances = distances;\r
115     }\r
116 \r
117     /**\r
118      * Creates a new NewickFile object.\r
119      *\r
120      * @param newtree DOCUMENT ME!\r
121      * @param bootstrap DOCUMENT ME!\r
122      * @param distances DOCUMENT ME!\r
123      * @param rootdistance DOCUMENT ME!\r
124      */\r
125     public NewickFile(SequenceNode newtree, boolean bootstrap,\r
126         boolean distances, boolean rootdistance)\r
127     {\r
128         root = newtree;\r
129         HasBootstrap = bootstrap;\r
130         HasDistances = distances;\r
131         RootHasDistance = rootdistance;\r
132     }\r
133 \r
134     /**\r
135      * DOCUMENT ME!\r
136      *\r
137      * @param Error DOCUMENT ME!\r
138      * @param Er DOCUMENT ME!\r
139      * @param r DOCUMENT ME!\r
140      * @param p DOCUMENT ME!\r
141      * @param s DOCUMENT ME!\r
142      *\r
143      * @return DOCUMENT ME!\r
144      */\r
145     private String ErrorStringrange(String Error, String Er, int r, int p,\r
146         String s)\r
147     {\r
148         return ((Error == null) ? "" : Error) + Er + " at position " + p +\r
149         " ( " +\r
150         s.substring(((p - r) < 0) ? 0 : (p - r),\r
151             ((p + r) > s.length()) ? s.length() : (p + r)) + " )\n";\r
152     }\r
153 \r
154     // @tree annotations\r
155     // These are set automatically by the reader\r
156     public boolean HasBootstrap()\r
157     {\r
158         return HasBootstrap;\r
159     }\r
160 \r
161     /**\r
162      * DOCUMENT ME!\r
163      *\r
164      * @return DOCUMENT ME!\r
165      */\r
166     public boolean HasDistances()\r
167     {\r
168         return HasDistances;\r
169     }\r
170 \r
171     /**\r
172      * DOCUMENT ME!\r
173      *\r
174      * @throws IOException DOCUMENT ME!\r
175      */\r
176     public void parse() throws IOException\r
177     {\r
178         String nf;\r
179 \r
180         { // fill nf with complete tree file\r
181 \r
182             StringBuffer file = new StringBuffer();\r
183 \r
184             while ((nf = nextLine()) != null)\r
185             {\r
186                 file.append(nf);\r
187             }\r
188 \r
189             nf = file.toString();\r
190         }\r
191 \r
192         root = new SequenceNode();\r
193 \r
194         SequenceNode realroot = null;\r
195         SequenceNode c = root;\r
196 \r
197         int d = -1;\r
198         int cp = 0;\r
199         int flen = nf.length();\r
200 \r
201         String Error = null;\r
202         String nodename = null;\r
203 \r
204         float DefDistance = (float) 0.00001; // @param Default distance for a node - very very small\r
205         int DefBootstrap = 0; // @param Default bootstrap for a node\r
206 \r
207         float distance = DefDistance;\r
208         int bootstrap = DefBootstrap;\r
209 \r
210         boolean ascending = false; // flag indicating that we are leaving the current node\r
211 \r
212         com.stevesoft.pat.Regex majorsyms = new com.stevesoft.pat.Regex(\r
213                 "[(\\['),;]");\r
214 \r
215         while (majorsyms.searchFrom(nf, cp) && (Error == null))\r
216         {\r
217             int fcp = majorsyms.matchedFrom();\r
218 \r
219             switch (nf.charAt(fcp))\r
220             {\r
221             case '[': // Comment or structured/extended NH format info\r
222 \r
223                 com.stevesoft.pat.Regex comment = new com.stevesoft.pat.Regex(\r
224                         "]");\r
225 \r
226                 if (comment.searchFrom(nf, fcp))\r
227                 {\r
228                     // Skip the comment field\r
229                     cp = 1 + comment.matchedFrom();\r
230                 }\r
231                 else\r
232                 {\r
233                     Error = ErrorStringrange(Error, "Unterminated comment", 3,\r
234                             fcp, nf);\r
235                 }\r
236 \r
237                 ;\r
238 \r
239                 break;\r
240 \r
241             case '(':\r
242 \r
243                 // ascending should not be set\r
244                 // New Internal node\r
245                 if (ascending)\r
246                 {\r
247                     Error = ErrorStringrange(Error, "Unexpected '('", 7, fcp, nf);\r
248 \r
249                     continue;\r
250                 }\r
251 \r
252                 ;\r
253                 d++;\r
254 \r
255                 if (c.right() == null)\r
256                 {\r
257                     c.setRight(new SequenceNode(null, c, null, DefDistance,\r
258                             DefBootstrap, false));\r
259                     c = (SequenceNode) c.right();\r
260                 }\r
261                 else\r
262                 {\r
263                     if (c.left() != null)\r
264                     {\r
265                         // Dummy node for polytomy - keeps c.left free for new node\r
266                         SequenceNode tmpn = new SequenceNode(null, c, null, 0,\r
267                                 0, true);\r
268                         tmpn.SetChildren(c.left(), c.right());\r
269                         c.setRight(tmpn);\r
270                     }\r
271 \r
272                     c.setLeft(new SequenceNode(null, c, null, DefDistance,\r
273                             DefBootstrap, false));\r
274                     c = (SequenceNode) c.left();\r
275                 }\r
276 \r
277                 if (realroot == null)\r
278                 {\r
279                     realroot = c;\r
280                 }\r
281 \r
282                 nodename = null;\r
283                 distance = DefDistance;\r
284                 bootstrap = DefBootstrap;\r
285                 cp = fcp + 1;\r
286 \r
287                 break;\r
288 \r
289             // Deal with quoted fields\r
290             case '\'':\r
291 \r
292                 com.stevesoft.pat.Regex qnodename = new com.stevesoft.pat.Regex(\r
293                         "([^']|'')+'");\r
294 \r
295                 if (qnodename.searchFrom(nf, fcp))\r
296                 {\r
297                     int nl = qnodename.stringMatched().length();\r
298                     nodename = new String(qnodename.stringMatched().substring(0,\r
299                                 nl - 1));\r
300                     cp = fcp + nl + 1;\r
301                 }\r
302                 else\r
303                 {\r
304                     Error = ErrorStringrange(Error,\r
305                             "Unterminated quotes for nodename", 7, fcp, nf);\r
306                 }\r
307 \r
308                 break;\r
309 \r
310             case ';':\r
311 \r
312                 if (d != -1)\r
313                 {\r
314                     Error = ErrorStringrange(Error,\r
315                             "Wayward semicolon (depth=" + d + ")", 7, fcp, nf);\r
316                 }\r
317 \r
318             // cp advanced at the end of default\r
319             default:\r
320 \r
321                 // Parse simpler field strings\r
322                 String fstring = nf.substring(cp, fcp);\r
323                 com.stevesoft.pat.Regex uqnodename = new com.stevesoft.pat.Regex(\r
324                         "\\b([^' :;\\](),]+)");\r
325                 com.stevesoft.pat.Regex nbootstrap = new com.stevesoft.pat.Regex(\r
326                         "\\S+([0-9+]+)\\S*:");\r
327                 com.stevesoft.pat.Regex ndist = new com.stevesoft.pat.Regex(\r
328                         ":([-0-9.+]+)");\r
329 \r
330                 if (uqnodename.search(fstring) &&\r
331                         ((uqnodename.matchedFrom(1) == 0) ||\r
332                         (fstring.charAt(uqnodename.matchedFrom(1) - 1) != ':'))) // JBPNote HACK!\r
333                 {\r
334                     if (nodename == null)\r
335                     {\r
336                         if (ReplaceUnderscores)\r
337                         {\r
338                             nodename = uqnodename.stringMatched(1).replace('_',\r
339                                     ' ');\r
340                         }\r
341                         else\r
342                         {\r
343                             nodename = uqnodename.stringMatched(1);\r
344                         }\r
345                     }\r
346                     else\r
347                     {\r
348                         Error = ErrorStringrange(Error,\r
349                                 "File has broken algorithm - overwritten nodename",\r
350                                 10, fcp, nf);\r
351                     }\r
352                 }\r
353 \r
354                 if (nbootstrap.search(fstring) &&\r
355                         (nbootstrap.matchedFrom(1) > (uqnodename.matchedFrom(1) +\r
356                         uqnodename.stringMatched().length())))\r
357                 {\r
358                     try\r
359                     {\r
360                         bootstrap = (new Integer(nbootstrap.stringMatched(1))).intValue();\r
361                         HasBootstrap = true;\r
362                     }\r
363                     catch (Exception e)\r
364                     {\r
365                         Error = ErrorStringrange(Error,\r
366                                 "Can't parse bootstrap value", 4,\r
367                                 cp + nbootstrap.matchedFrom(), nf);\r
368                     }\r
369                 }\r
370 \r
371                 boolean nodehasdistance = false;\r
372 \r
373                 if (ndist.search(fstring))\r
374                 {\r
375                     try\r
376                     {\r
377                         distance = (new Float(ndist.stringMatched(1))).floatValue();\r
378                         HasDistances = true;\r
379                         nodehasdistance = true;\r
380                     }\r
381                     catch (Exception e)\r
382                     {\r
383                         Error = ErrorStringrange(Error,\r
384                                 "Can't parse node distance value", 7,\r
385                                 cp + ndist.matchedFrom(), nf);\r
386                     }\r
387                 }\r
388 \r
389                 if (ascending)\r
390                 {\r
391                     // Write node info here\r
392                     c.setName(nodename);\r
393                     c.dist = (HasDistances) ? distance : 0;\r
394                     c.setBootstrap((HasBootstrap) ? bootstrap : 0);\r
395 \r
396                     if (c == realroot)\r
397                     {\r
398                         RootHasDistance = nodehasdistance; // JBPNote This is really UGLY!!!\r
399                     }\r
400                 }\r
401                 else\r
402                 {\r
403                     // Find a place to put the leaf\r
404                     SequenceNode newnode = new SequenceNode(null, c, nodename,\r
405                             (HasDistances) ? distance : DefDistance,\r
406                             (HasBootstrap) ? bootstrap : DefBootstrap, false);\r
407 \r
408                     if (c.right() == null)\r
409                     {\r
410                         c.setRight(newnode);\r
411                     }\r
412                     else\r
413                     {\r
414                         if (c.left() == null)\r
415                         {\r
416                             c.setLeft(newnode);\r
417                         }\r
418                         else\r
419                         {\r
420                             // Insert a dummy node for polytomy\r
421                             SequenceNode newdummy = new SequenceNode(null, c,\r
422                                     null, 0, 0, true);\r
423                             newdummy.SetChildren(c.left(), newnode);\r
424                             c.setLeft(newdummy);\r
425                         }\r
426                     }\r
427                 }\r
428 \r
429                 if (ascending)\r
430                 {\r
431                     // move back up the tree from preceding closure\r
432                     c = c.AscendTree();\r
433 \r
434                     if ((d > -1) && (c == null))\r
435                     {\r
436                         Error = ErrorStringrange(Error,\r
437                                 "File broke algorithm: Lost place in tree (is there an extra ')' ?)",\r
438                                 7, fcp, nf);\r
439                     }\r
440                 }\r
441 \r
442                 if (nf.charAt(fcp) == ')')\r
443                 {\r
444                     d--;\r
445                     ascending = true;\r
446                 }\r
447                 else\r
448                 {\r
449                     if (nf.charAt(fcp) == ',')\r
450                     {\r
451                         if (ascending)\r
452                         {\r
453                             ascending = false;\r
454                         }\r
455                         else\r
456                         {\r
457                             // Just advance focus, if we need to\r
458                             if ((c.left() != null) && (!c.left().isLeaf()))\r
459                             {\r
460                                 c = (SequenceNode) c.left();\r
461                             }\r
462                         }\r
463                     }\r
464 \r
465                     // else : We do nothing if ';' is encountered.\r
466                 }\r
467 \r
468                 // Reset new node properties to obvious fakes\r
469                 nodename = null;\r
470                 distance = DefDistance;\r
471                 bootstrap = DefBootstrap;\r
472 \r
473                 cp = fcp + 1;\r
474             }\r
475         }\r
476 \r
477         if (Error != null)\r
478         {\r
479             throw (new IOException("NewickFile: " + Error + "\n"));\r
480         }\r
481 \r
482         root = (SequenceNode) root.right().detach(); // remove the imaginary root.\r
483 \r
484         if (!RootHasDistance)\r
485         {\r
486             root.dist = 0;\r
487         }\r
488     }\r
489 \r
490     /**\r
491      * DOCUMENT ME!\r
492      *\r
493      * @return DOCUMENT ME!\r
494      */\r
495     public SequenceNode getTree()\r
496     {\r
497         return root;\r
498     }\r
499 \r
500     /**\r
501      * DOCUMENT ME!\r
502      *\r
503      * @return DOCUMENT ME!\r
504      */\r
505     public String print()\r
506     {\r
507         synchronized (this)\r
508         {\r
509             StringBuffer tf = new StringBuffer();\r
510             print(tf, root);\r
511 \r
512             return (tf.append(";").toString());\r
513         }\r
514     }\r
515 \r
516     /**\r
517      * DOCUMENT ME!\r
518      *\r
519      * @param withbootstraps DOCUMENT ME!\r
520      *\r
521      * @return DOCUMENT ME!\r
522      */\r
523     public String print(boolean withbootstraps)\r
524     {\r
525         synchronized (this)\r
526         {\r
527             boolean boots = this.HasBootstrap;\r
528             this.HasBootstrap = withbootstraps;\r
529 \r
530             String rv = print();\r
531             this.HasBootstrap = boots;\r
532 \r
533             return rv;\r
534         }\r
535     }\r
536 \r
537     /**\r
538      * DOCUMENT ME!\r
539      *\r
540      * @param withbootstraps DOCUMENT ME!\r
541      * @param withdists DOCUMENT ME!\r
542      *\r
543      * @return DOCUMENT ME!\r
544      */\r
545     public String print(boolean withbootstraps, boolean withdists)\r
546     {\r
547         synchronized (this)\r
548         {\r
549             boolean dists = this.HasDistances;\r
550             this.HasDistances = withdists;\r
551 \r
552             String rv = print(withbootstraps);\r
553             this.HasDistances = dists;\r
554 \r
555             return rv;\r
556         }\r
557     }\r
558 \r
559     /**\r
560      * DOCUMENT ME!\r
561      *\r
562      * @param withbootstraps DOCUMENT ME!\r
563      * @param withdists DOCUMENT ME!\r
564      * @param printRootInfo DOCUMENT ME!\r
565      *\r
566      * @return DOCUMENT ME!\r
567      */\r
568     public String print(boolean withbootstraps, boolean withdists,\r
569         boolean printRootInfo)\r
570     {\r
571         synchronized (this)\r
572         {\r
573             boolean rootinfo = printRootInfo;\r
574             this.printRootInfo = printRootInfo;\r
575 \r
576             String rv = print(withbootstraps, withdists);\r
577             this.printRootInfo = rootinfo;\r
578 \r
579             return rv;\r
580         }\r
581     }\r
582 \r
583     /**\r
584      * DOCUMENT ME!\r
585      *\r
586      * @return DOCUMENT ME!\r
587      */\r
588     char getQuoteChar()\r
589     {\r
590         return QuoteChar;\r
591     }\r
592 \r
593     /**\r
594      * DOCUMENT ME!\r
595      *\r
596      * @param c DOCUMENT ME!\r
597      *\r
598      * @return DOCUMENT ME!\r
599      */\r
600     char setQuoteChar(char c)\r
601     {\r
602         char old = QuoteChar;\r
603         QuoteChar = c;\r
604 \r
605         return old;\r
606     }\r
607 \r
608     /**\r
609      * DOCUMENT ME!\r
610      *\r
611      * @param name DOCUMENT ME!\r
612      *\r
613      * @return DOCUMENT ME!\r
614      */\r
615     private String nodeName(String name)\r
616     {\r
617         if (NodeSafeName[0].search(name))\r
618         {\r
619             return QuoteChar + NodeSafeName[1].replaceAll(name) + QuoteChar;\r
620         }\r
621         else\r
622         {\r
623             return NodeSafeName[2].replaceAll(name);\r
624         }\r
625     }\r
626 \r
627     /**\r
628      * DOCUMENT ME!\r
629      *\r
630      * @param c DOCUMENT ME!\r
631      *\r
632      * @return DOCUMENT ME!\r
633      */\r
634     private String printNodeField(SequenceNode c)\r
635     {\r
636         return ((c.getName() == null) ? "" : nodeName(c.getName())) +\r
637         ((HasBootstrap)\r
638         ? ((c.getBootstrap() > -1) ? (" " + c.getBootstrap()) : "") : "") +\r
639         ((HasDistances) ? (":" + c.dist) : "");\r
640     }\r
641 \r
642     /**\r
643      * DOCUMENT ME!\r
644      *\r
645      * @param root DOCUMENT ME!\r
646      *\r
647      * @return DOCUMENT ME!\r
648      */\r
649     private String printRootField(SequenceNode root)\r
650     {\r
651         return (printRootInfo)\r
652         ? (((root.getName() == null) ? "" : nodeName(root.getName())) +\r
653         ((HasBootstrap)\r
654         ? ((root.getBootstrap() > -1) ? (" " + root.getBootstrap()) : "") : "") +\r
655         ((RootHasDistance) ? (":" + root.dist) : "")) : "";\r
656     }\r
657 \r
658     // Non recursive call deals with root node properties\r
659     public void print(StringBuffer tf, SequenceNode root)\r
660     {\r
661         if (root != null)\r
662         {\r
663             if (root.isLeaf() && printRootInfo)\r
664             {\r
665                 tf.append(printRootField(root));\r
666             }\r
667             else\r
668             {\r
669                 if (root.isDummy())\r
670                 {\r
671                     _print(tf, (SequenceNode) root.right());\r
672                     _print(tf, (SequenceNode) root.left());\r
673                 }\r
674                 else\r
675                 {\r
676                     tf.append("(");\r
677                     _print(tf, (SequenceNode) root.right());\r
678 \r
679                     if (root.left() != null)\r
680                     {\r
681                         tf.append(",");\r
682                     }\r
683 \r
684                     _print(tf, (SequenceNode) root.left());\r
685                     tf.append(")" + printRootField(root));\r
686                 }\r
687             }\r
688         }\r
689     }\r
690 \r
691     // Recursive call for non-root nodes\r
692     public void _print(StringBuffer tf, SequenceNode c)\r
693     {\r
694         if (c != null)\r
695         {\r
696             if (c.isLeaf())\r
697             {\r
698                 tf.append(printNodeField(c));\r
699             }\r
700             else\r
701             {\r
702                 if (c.isDummy())\r
703                 {\r
704                     _print(tf, (SequenceNode) c.right());\r
705                     _print(tf, (SequenceNode) c.left());\r
706                 }\r
707                 else\r
708                 {\r
709                     tf.append("(");\r
710                     _print(tf, (SequenceNode) c.right());\r
711 \r
712                     if (c.left() != null)\r
713                     {\r
714                         tf.append(",");\r
715                     }\r
716 \r
717                     _print(tf, (SequenceNode) c.left());\r
718                     tf.append(")" + printNodeField(c));\r
719                 }\r
720             }\r
721         }\r
722     }\r
723 \r
724     // Test\r
725     public static void main(String[] args)\r
726     {\r
727         try\r
728         {\r
729             File fn = new File(args[0]);\r
730 \r
731             StringBuffer newickfile = new StringBuffer();\r
732             BufferedReader treefile = new BufferedReader(new FileReader(fn));\r
733             String l;\r
734 \r
735             while ((l = treefile.readLine()) != null)\r
736             {\r
737                 newickfile.append(l);\r
738             }\r
739 \r
740             treefile.close();\r
741             System.out.println("Read file :\n");\r
742 \r
743             NewickFile trf = new NewickFile(args[0], "File");\r
744             trf.parse();\r
745             System.out.println("Original file :\n");\r
746 \r
747             com.stevesoft.pat.Regex nonl = new com.stevesoft.pat.Regex("\n+", "");\r
748             System.out.println(nonl.replaceAll(newickfile.toString()) + "\n");\r
749 \r
750             System.out.println("Parsed file.\n");\r
751             System.out.println("Default output type for original input.\n");\r
752             System.out.println(trf.print());\r
753             System.out.println("Without bootstraps.\n");\r
754             System.out.println(trf.print(false));\r
755             System.out.println("Without distances.\n");\r
756             System.out.println(trf.print(true, false));\r
757             System.out.println("Without bootstraps but with distanecs.\n");\r
758             System.out.println(trf.print(false, true));\r
759             System.out.println("Without bootstraps or distanecs.\n");\r
760             System.out.println(trf.print(false, false));\r
761             System.out.println("With bootstraps and with distances.\n");\r
762             System.out.println(trf.print(true, true));\r
763         }\r
764         catch (java.io.IOException e)\r
765         {\r
766             System.err.println("Exception\n" + e);\r
767             e.printStackTrace();\r
768         }\r
769     }\r
770 }\r