View Javadoc

1   /**
2      This file is part of GoldenGate Project (named also GoldenGate or GG).
3   
4      Copyright 2009, Frederic Bregier, and individual contributors by the @author
5      tags. See the COPYRIGHT.txt in the distribution for a full listing of
6      individual contributors.
7   
8      All GoldenGate Project is free software: you can redistribute it and/or 
9      modify it under the terms of the GNU General Public License as published 
10     by the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12  
13     GoldenGate is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17  
18     You should have received a copy of the GNU General Public License
19     along with GoldenGate .  If not, see <http://www.gnu.org/licenses/>.
20   */
21  package openr66.r66gui;
22  
23  import java.awt.Color;
24  import java.awt.Component;
25  import java.awt.EventQueue;
26  import java.awt.GridBagConstraints;
27  import java.awt.GridBagLayout;
28  import java.awt.Insets;
29  import java.awt.event.ActionEvent;
30  import java.awt.event.ActionListener;
31  import java.awt.event.FocusAdapter;
32  import java.awt.event.FocusEvent;
33  import java.awt.event.WindowAdapter;
34  import java.awt.event.WindowEvent;
35  import java.io.File;
36  import java.io.OutputStream;
37  import java.io.PrintStream;
38  
39  import javax.swing.JButton;
40  import javax.swing.JCheckBox;
41  import javax.swing.JComboBox;
42  import javax.swing.JEditorPane;
43  import javax.swing.JFileChooser;
44  import javax.swing.JFrame;
45  import javax.swing.JLabel;
46  import javax.swing.JMenu;
47  import javax.swing.JMenuBar;
48  import javax.swing.JMenuItem;
49  import javax.swing.JProgressBar;
50  import javax.swing.JScrollPane;
51  import javax.swing.JSeparator;
52  import javax.swing.JTextArea;
53  import javax.swing.JTextField;
54  import javax.swing.SwingWorker;
55  import javax.swing.border.BevelBorder;
56  import javax.swing.border.SoftBevelBorder;
57  
58  import com.swtdesigner.FocusTraversalOnArray;
59  
60  /**
61   * R66 Client GUI to show how to use the API and also to enable to test the connectivity to R66 servers 
62   * and the validity of a transfer through a rule.
63   * 
64   * @author Frederic Bregier
65   *
66   */
67  public class R66ClientGui {
68  
69      public static String []static_args;
70      public static R66ClientGui window;
71      
72      private JFrame frmRClientGui;
73      private JTextField textFieldInformation;
74      private JTextField textFieldFile;
75      private R66Environment environnement = new R66Environment();
76      private JEditorPane textFieldStatus;
77      private JComboBox comboBoxHosts;
78      private JComboBox comboBoxRules;
79      private JCheckBox checkBoxMD5;
80      private JProgressBar progressBarTransfer;
81      private JButton buttonTransferStart;
82      private JMenu menu;
83      private JButton buttonCheckConnection;
84      private JButton buttonFileFind;
85      private R66Dialog dialog;
86      private JTextArea textPaneLog;
87      private JScrollPane scrollPane;
88      private JScrollPane scrollPane_1;
89      private JCheckBox checkBoxDebug;
90      
91      /**
92       * Launch the application.
93       */
94      public static void main(String[] args) {
95          static_args = args;
96          EventQueue.invokeLater(new Runnable() {
97              public void run() {
98                  try {
99                      window = new R66ClientGui(static_args);
100                     window.frmRClientGui.setVisible(true);
101                 } catch (Exception e) {
102                     e.printStackTrace();
103                 }
104             }
105         });
106     }
107 
108     /**
109      * Create the application.
110      */
111     public R66ClientGui(String []args) {
112         environnement.initialize(args);
113         initialize();
114     }
115 
116     /**
117      * Initialize the contents of the frame.
118      */
119     private void initialize() {
120         String [] shosts = R66Environment.getHostIds();
121         String [] srules = R66Environment.getRules();
122         
123         frmRClientGui = new JFrame();
124         frmRClientGui.addWindowListener(new WindowAdapter() {
125             @Override
126             public void windowClosing(WindowEvent e) {
127                 environnement.exit();
128                 System.exit(0);
129             }
130         });
131         frmRClientGui.setTitle("R66 Client Gui");
132         frmRClientGui.setBounds(100, 100, 724, 546);
133         frmRClientGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
134         
135         JMenuBar menuBar = new JMenuBar();
136         frmRClientGui.setJMenuBar(menuBar);
137         
138         menu = new JMenu("Menu");
139         menuBar.add(menu);
140         
141         JMenuItem menuItemExit = new JMenuItem("Exit");
142         menuItemExit.addActionListener(new ActionListener() {
143             public void actionPerformed(ActionEvent e) {
144                 environnement.exit();
145                 System.exit(0);
146             }
147         });
148         menu.add(menuItemExit);
149         
150         JSeparator separator = new JSeparator();
151         menu.add(separator);
152         
153         JMenuItem menuItemHelp = new JMenuItem("Help");
154         menuItemHelp.addActionListener(new ActionListener() {
155             public void actionPerformed(ActionEvent e) {
156                 environnement.about();
157                 showDialog();
158             }
159         });
160         menu.add(menuItemHelp);
161         GridBagLayout gridBagLayout = new GridBagLayout();
162         gridBagLayout.columnWidths = new int[]{24, 130, 80, 369, 99, 0};
163         gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 27, 179, 162};
164         gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
165         gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0};
166         frmRClientGui.getContentPane().setLayout(gridBagLayout);
167         
168         buttonCheckConnection = new JButton("Check Connection");
169         buttonCheckConnection.setToolTipText("Check the connectivity with the selected Host by sending a simple message");
170         buttonCheckConnection.addActionListener(new ActionListener() {
171             public void actionPerformed(ActionEvent e) {
172                 R66ClientGuiActions action = new R66ClientGuiActions(R66ClientGuiActions.CHECKCONNECTION);
173                 action.execute();
174             }
175         });
176         GridBagConstraints gbc_buttonCheckConnection = new GridBagConstraints();
177         gbc_buttonCheckConnection.insets = new Insets(0, 0, 5, 5);
178         gbc_buttonCheckConnection.gridx = 1;
179         gbc_buttonCheckConnection.gridy = 0;
180         frmRClientGui.getContentPane().add(buttonCheckConnection, gbc_buttonCheckConnection);
181         
182         JLabel label = new JLabel("Host Id");
183         GridBagConstraints gbc_label = new GridBagConstraints();
184         gbc_label.insets = new Insets(0, 0, 5, 5);
185         gbc_label.gridx = 2;
186         gbc_label.gridy = 0;
187         frmRClientGui.getContentPane().add(label, gbc_label);
188         
189         comboBoxHosts = new JComboBox(shosts);
190         comboBoxHosts.addActionListener(new ActionListener() {
191             public void actionPerformed(ActionEvent e) {
192                 environnement.GuiResultat = R66Environment.getHost((String) comboBoxHosts.getSelectedItem());
193                 setStatus(environnement.GuiResultat);
194             }
195         });
196         label.setLabelFor(comboBoxHosts);
197         comboBoxHosts.setToolTipText("Select a host on which you want to test the connectivity or send a request of transfer");
198         GridBagConstraints gbc_comboBoxHosts = new GridBagConstraints();
199         gbc_comboBoxHosts.fill = GridBagConstraints.HORIZONTAL;
200         gbc_comboBoxHosts.insets = new Insets(0, 0, 5, 5);
201         gbc_comboBoxHosts.gridx = 3;
202         gbc_comboBoxHosts.gridy = 0;
203         frmRClientGui.getContentPane().add(comboBoxHosts, gbc_comboBoxHosts);
204         
205         JLabel label_1 = new JLabel("Rule");
206         GridBagConstraints gbc_label_1 = new GridBagConstraints();
207         gbc_label_1.insets = new Insets(0, 0, 5, 5);
208         gbc_label_1.gridx = 2;
209         gbc_label_1.gridy = 1;
210         frmRClientGui.getContentPane().add(label_1, gbc_label_1);
211         
212         comboBoxRules = new JComboBox(srules);
213         comboBoxRules.addActionListener(new ActionListener() {
214             public void actionPerformed(ActionEvent e) {
215                 environnement.GuiResultat = R66Environment.getRule((String) comboBoxRules.getSelectedItem());
216                 setStatus(environnement.GuiResultat);
217             }
218         });
219         label_1.setLabelFor(comboBoxRules);
220         comboBoxRules.setToolTipText("Select a Rule to use in case if a request of transfer");
221         GridBagConstraints gbc_comboBoxRules = new GridBagConstraints();
222         gbc_comboBoxRules.fill = GridBagConstraints.HORIZONTAL;
223         gbc_comboBoxRules.insets = new Insets(0, 0, 5, 5);
224         gbc_comboBoxRules.gridx = 3;
225         gbc_comboBoxRules.gridy = 1;
226         frmRClientGui.getContentPane().add(comboBoxRules, gbc_comboBoxRules);
227         
228         checkBoxMD5 = new JCheckBox("MD5");
229         checkBoxMD5.setToolTipText("Checked if you want that all packets are checked using MD5 (optional and not recommended if already using SSL)");
230         GridBagConstraints gbc_checkBoxMD5 = new GridBagConstraints();
231         gbc_checkBoxMD5.insets = new Insets(0, 0, 5, 0);
232         gbc_checkBoxMD5.gridx = 4;
233         gbc_checkBoxMD5.gridy = 1;
234         frmRClientGui.getContentPane().add(checkBoxMD5, gbc_checkBoxMD5);
235         
236         JLabel label_2 = new JLabel("Information");
237         GridBagConstraints gbc_label_2 = new GridBagConstraints();
238         gbc_label_2.insets = new Insets(0, 0, 5, 5);
239         gbc_label_2.gridx = 2;
240         gbc_label_2.gridy = 2;
241         frmRClientGui.getContentPane().add(label_2, gbc_label_2);
242         
243         textFieldInformation = new JTextField();
244         label_2.setLabelFor(textFieldInformation);
245         textFieldInformation.setToolTipText("Information to provide in the field info of the request to the remote host");
246         GridBagConstraints gbc_textFieldInformation = new GridBagConstraints();
247         gbc_textFieldInformation.weightx = 1.0;
248         gbc_textFieldInformation.fill = GridBagConstraints.HORIZONTAL;
249         gbc_textFieldInformation.gridwidth = 2;
250         gbc_textFieldInformation.insets = new Insets(0, 0, 5, 0);
251         gbc_textFieldInformation.gridx = 3;
252         gbc_textFieldInformation.gridy = 2;
253         frmRClientGui.getContentPane().add(textFieldInformation, gbc_textFieldInformation);
254         textFieldInformation.setColumns(10);
255         
256         JLabel label_3 = new JLabel("File");
257         GridBagConstraints gbc_label_3 = new GridBagConstraints();
258         gbc_label_3.insets = new Insets(0, 0, 5, 5);
259         gbc_label_3.gridx = 2;
260         gbc_label_3.gridy = 3;
261         frmRClientGui.getContentPane().add(label_3, gbc_label_3);
262         
263         textFieldFile = new JTextField();
264         textFieldFile.addFocusListener(new FocusAdapter() {
265             @Override
266             public void focusLost(FocusEvent e) {
267                 setFindFile();
268             }
269         });
270         label_3.setLabelFor(textFieldFile);
271         textFieldFile.setToolTipText("File reference to send or receive. It might be a full path or a relative path. Note that \" \" might be necessary if blank characters occur.");
272         GridBagConstraints gbc_textFieldFile = new GridBagConstraints();
273         gbc_textFieldFile.fill = GridBagConstraints.HORIZONTAL;
274         gbc_textFieldFile.insets = new Insets(0, 0, 5, 5);
275         gbc_textFieldFile.gridx = 3;
276         gbc_textFieldFile.gridy = 3;
277         frmRClientGui.getContentPane().add(textFieldFile, gbc_textFieldFile);
278         textFieldFile.setColumns(10);
279         
280         buttonFileFind = new JButton("File Find");
281         buttonFileFind.setToolTipText("Helper to find a local file to send");
282         buttonFileFind.addActionListener(new ActionListener() {
283             public void actionPerformed(ActionEvent e) {
284                 R66ClientGuiActions action = new R66ClientGuiActions(R66ClientGuiActions.FILESELECT);
285                 action.execute();
286             }
287         });
288         GridBagConstraints gbc_buttonFileFind = new GridBagConstraints();
289         gbc_buttonFileFind.insets = new Insets(0, 0, 5, 0);
290         gbc_buttonFileFind.gridx = 4;
291         gbc_buttonFileFind.gridy = 3;
292         frmRClientGui.getContentPane().add(buttonFileFind, gbc_buttonFileFind);
293         
294         buttonTransferStart = new JButton("Starts Transfer");
295         buttonTransferStart.setToolTipText("Starts the request of transfer according to the above options");
296         buttonTransferStart.addActionListener(new ActionListener() {
297             public void actionPerformed(ActionEvent e) {
298                 R66ClientGuiActions action = new R66ClientGuiActions(R66ClientGuiActions.STARTTRANSFER);
299                 action.execute();
300             }
301         });
302         GridBagConstraints gbc_buttonTransferStart = new GridBagConstraints();
303         gbc_buttonTransferStart.insets = new Insets(0, 0, 5, 5);
304         gbc_buttonTransferStart.gridx = 3;
305         gbc_buttonTransferStart.gridy = 4;
306         frmRClientGui.getContentPane().add(buttonTransferStart, gbc_buttonTransferStart);
307         
308         checkBoxDebug = new JCheckBox("Debug");
309         checkBoxDebug.addActionListener(new ActionListener() {
310             public void actionPerformed(ActionEvent e) {
311                 environnement.debug(checkBoxDebug.isSelected());
312             }
313         });
314         GridBagConstraints gbc_checkBoxDebug = new GridBagConstraints();
315         gbc_checkBoxDebug.insets = new Insets(0, 0, 5, 5);
316         gbc_checkBoxDebug.gridx = 1;
317         gbc_checkBoxDebug.gridy = 5;
318         environnement.debug(checkBoxDebug.isSelected());
319         frmRClientGui.getContentPane().add(checkBoxDebug, gbc_checkBoxDebug);
320         
321         progressBarTransfer = new JProgressBar();
322         GridBagConstraints gbc_progressBarTransfer = new GridBagConstraints();
323         gbc_progressBarTransfer.weightx = 1.0;
324         gbc_progressBarTransfer.fill = GridBagConstraints.HORIZONTAL;
325         gbc_progressBarTransfer.insets = new Insets(0, 0, 5, 0);
326         gbc_progressBarTransfer.gridwidth = 3;
327         gbc_progressBarTransfer.gridx = 2;
328         gbc_progressBarTransfer.gridy = 5;
329         frmRClientGui.getContentPane().add(progressBarTransfer, gbc_progressBarTransfer);
330         progressBarTransfer.setVisible(false);
331         
332         scrollPane_1 = new JScrollPane();
333         scrollPane_1.setViewportBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
334         GridBagConstraints gbc_scrollPane_1 = new GridBagConstraints();
335         gbc_scrollPane_1.weighty = 1.0;
336         gbc_scrollPane_1.weightx = 1.0;
337         gbc_scrollPane_1.fill = GridBagConstraints.BOTH;
338         gbc_scrollPane_1.gridwidth = 5;
339         gbc_scrollPane_1.insets = new Insets(0, 0, 5, 0);
340         gbc_scrollPane_1.gridx = 0;
341         gbc_scrollPane_1.gridy = 6;
342         frmRClientGui.getContentPane().add(scrollPane_1, gbc_scrollPane_1);
343         
344         textFieldStatus = new JEditorPane();
345         textFieldStatus.setToolTipText("Result of last command");
346         scrollPane_1.setViewportView(textFieldStatus);
347         textFieldStatus.setForeground(Color.GRAY);
348         textFieldStatus.setBackground(new Color(255, 255, 153));
349         textFieldStatus.setContentType("text/html");
350         textFieldStatus.setEditable(false);
351         
352         scrollPane = new JScrollPane();
353         scrollPane.setViewportBorder(new SoftBevelBorder(BevelBorder.LOWERED, null, null, null, null));
354         GridBagConstraints gbc_scrollPane = new GridBagConstraints();
355         gbc_scrollPane.weighty = 1.0;
356         gbc_scrollPane.weightx = 1.0;
357         gbc_scrollPane.fill = GridBagConstraints.BOTH;
358         gbc_scrollPane.gridwidth = 5;
359         gbc_scrollPane.gridx = 0;
360         gbc_scrollPane.gridy = 7;
361         frmRClientGui.getContentPane().add(scrollPane, gbc_scrollPane);
362         
363         textPaneLog = new JTextArea();
364         scrollPane.setViewportView(textPaneLog);
365         textPaneLog.setToolTipText("Output of internal commands of R66");
366         textPaneLog.setEditable(false);
367         
368         System.setOut(new PrintStream(new JTextAreaOutputStream(textPaneLog)));
369         frmRClientGui.getContentPane().setFocusTraversalPolicy(new FocusTraversalOnArray(new Component[]{buttonCheckConnection, comboBoxHosts, comboBoxRules, checkBoxMD5, textFieldInformation, textFieldFile, buttonFileFind, buttonTransferStart}));
370     }
371 
372     /**
373      * @author Frederic Bregier
374      *
375      */
376     public class R66ClientGuiActions extends SwingWorker<String, Integer>{
377         static final int CHECKCONNECTION = 1;
378         static final int STARTTRANSFER = 2;
379         static final int FILESELECT = 3;
380         int method;
381         R66ClientGuiActions(int method) {
382             this.method = method;
383         }
384         /* (non-Javadoc)
385          * @see javax.swing.SwingWorker#doInBackground()
386          */
387         @Override
388         protected String doInBackground() throws Exception {
389             disableAllButtons();
390             startRequest();
391             switch (method) {
392                 case CHECKCONNECTION:
393                     checkConnection();
394                     break;
395                 case STARTTRANSFER:
396                     startTransfer();
397                     break;
398                 case FILESELECT:
399                     findFile();
400                     break;
401                 default:
402                     environnement.GuiResultat = "Action not recognized";
403             }
404             setStatus(environnement.GuiResultat);
405             if (method != FILESELECT) {
406                 showDialog();
407             } else {
408                 enableAllButtons();
409             }
410             stopRequest();
411             return environnement.GuiResultat;
412         }
413     }
414     
415     private void showDialog() {
416         disableAllButtons();
417         if (dialog != null) {
418             dialog.dispose();
419             dialog = null;
420         }
421         if (dialog == null) {
422             dialog = new R66Dialog();
423             dialog.setLocationRelativeTo(frmRClientGui);
424             if (dialog.isAlwaysOnTopSupported()) {
425                 dialog.setAlwaysOnTop(true);
426             } else {
427                 dialog.toFront();
428             }
429         }
430         dialog.textPaneDialog.setText(environnement.GuiResultat);
431         dialog.setVisible(true);
432         dialog.requestFocus();
433     }
434     private void setStatus(String mesg) {
435         textFieldStatus.setText(mesg);
436     }
437     private void startRequest() {
438         progressBarTransfer.setIndeterminate(true);
439         progressBarTransfer.setValue(0);
440         progressBarTransfer.setVisible(true);
441         textPaneLog.setText("");
442     }
443     private void stopRequest() {
444         progressBarTransfer.setIndeterminate(true);
445         progressBarTransfer.setValue(0);
446         progressBarTransfer.setVisible(false);
447         frmRClientGui.toFront();
448         frmRClientGui.requestFocus();
449     }
450     private void checkConnection() {
451         startRequest();
452         disableAllButtons();
453         environnement.hostId = (String) comboBoxHosts.getSelectedItem();
454         if (environnement.hostId == null || environnement.hostId.trim().length() <= 0) {
455             environnement.hostId = "NO HOST";
456             environnement.GuiResultat = "No Host specified!";
457         } else {
458             environnement.hostId = environnement.hostId.trim();
459             environnement.checkConnection();
460         }
461         setStatus(environnement.GuiResultat);
462         showDialog();
463         stopRequest();
464     }
465     private void startTransfer() {
466         disableAllButtons();
467         environnement.hostId = (String) comboBoxHosts.getSelectedItem();
468         environnement.ruleId = (String) comboBoxRules.getSelectedItem();
469         environnement.filePath = textFieldFile.getText();
470         environnement.information = textFieldInformation.getText();
471         environnement.isMD5 = checkBoxMD5.isSelected();
472         
473         boolean ok = true;
474         if (environnement.hostId == null || environnement.hostId.trim().length() <= 0) {
475             environnement.hostId = "NO HOST";
476             ok = false;
477         } else {
478             environnement.hostId = environnement.hostId.trim();
479         }
480         if (environnement.filePath == null || environnement.filePath.trim().length() <= 0) {
481             environnement.filePath = "NO FILE";
482             ok = false;
483         } else {
484             environnement.filePath = environnement.filePath.trim();
485         }
486         if (environnement.information == null || environnement.information.trim().length() <= 0) {
487             environnement.information = "";
488         } else {
489             environnement.information = environnement.information.trim();
490         }
491         if (environnement.ruleId == null || environnement.ruleId.trim().length() <= 0) {
492             environnement.ruleId = "NO RULE";
493             ok = false;
494         } else {
495             environnement.ruleId = environnement.ruleId.trim();
496         }
497         if (ok) {
498             environnement.startsTransfer(progressBarTransfer, textFieldStatus);
499         } else {
500             environnement.GuiResultat = "<html>Not enough arg to start the transfer:<br>   "+
501                 "HostId: "+environnement.hostId+" Rule: "+environnement.ruleId+
502                 " File: "+environnement.filePath;
503         }
504         setStatus(environnement.GuiResultat);
505         showDialog();
506     }
507     private void findFile() {
508         startRequest();
509         disableAllButtons();
510         try {
511             JFileChooser fc = new JFileChooser();
512             int returnVal = fc.showOpenDialog(frmRClientGui);
513             if (returnVal == JFileChooser.APPROVE_OPTION) {
514                 File file = fc.getSelectedFile();
515                 textFieldFile.setText(file.getAbsolutePath());
516                 setFindFile();
517                 environnement.GuiResultat = "New file sets";
518             }
519         } finally {
520             enableAllButtons();
521             stopRequest();
522         }
523     }
524     private void setFindFile() {
525         String text = null;
526         try {
527             text = textFieldFile.getText();
528         } catch (NullPointerException e1) {
529             text = null;
530         }
531         if (text != null) {
532             if (text.contains(" ")) {
533                 if (!text.startsWith("\"")) {
534                     text = "\""+text;
535                 }
536                 if (!text.endsWith("\"")) {
537                     text = text+"\"";
538                 }
539                 textFieldFile.setText(text);
540             }
541         }
542     }
543     public void disableAllButtons() {
544         //frmRClientGui.setEnabled(false);
545         buttonCheckConnection.setEnabled(false);
546         buttonFileFind.setEnabled(false);
547         buttonTransferStart.setEnabled(false);
548         menu.setEnabled(false);
549         textFieldInformation.setEnabled(false);
550         textFieldFile.setEnabled(false);
551         comboBoxHosts.setEnabled(false);
552         comboBoxRules.setEnabled(false);
553         checkBoxMD5.setEnabled(false);
554         checkBoxDebug.setEnabled(false);
555     }
556     public void enableAllButtons() {
557         //frmRClientGui.setEnabled(true);
558         buttonCheckConnection.setEnabled(true);
559         buttonFileFind.setEnabled(true);
560         buttonTransferStart.setEnabled(true);
561         menu.setEnabled(true);
562         textFieldInformation.setEnabled(true);
563         textFieldFile.setEnabled(true);
564         comboBoxHosts.setEnabled(true);
565         comboBoxRules.setEnabled(true);
566         checkBoxMD5.setEnabled(true);
567         checkBoxDebug.setEnabled(true);
568         frmRClientGui.toFront();
569     }
570     
571     public class JTextAreaOutputStream extends OutputStream {
572         JTextArea ta;
573 
574         public JTextAreaOutputStream(JTextArea t) {
575           super();
576           ta = t;
577         }
578 
579         public void write(int i) {
580           ta.append(Character.toString((char)i));
581         }
582 
583         public void write(char[] buf, int off, int len) {
584           String s = new String(buf, off, len);
585           ta.append(s);
586         }
587 
588       }
589 }