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