001package bradleyross.common;
002import java.io.*; 
003import java.net.*; 
004import java.awt.*;
005import java.awt.Button;
006import java.awt.event.*;
007/**
008* This class opens a window for communications
009* with a port on another system.
010*
011* @author Bradley Ross
012*/
013public class communicationsWindow { 
014/**
015* Determines amount of diagnostic material to be
016* printed.
017*/
018private int debugLevel = 0;
019private  static int windowNumber = 0;
020// private  int thisWindow;
021private  FlowLayout layout;
022private  Socket sock = null; 
023private  PrintWriter pw = null; 
024private  BufferedReader in = null;
025private  InputStreamReader streamReader = null; 
026private  String logFileName="communicationsWindowLog.txt";
027/**
028* Determines whether messages are written to the log file.
029*/
030private  boolean logFileActive = false;
031private  java.io.FileWriter logFile;
032private  java.io.BufferedWriter logBuffer;
033/**
034* Used for writing the communicated text to a log file.
035*/
036private  java.io.PrintWriter logWriter;
037/*
038** The following items are used to determine when the
039** items in the window should be resized.
040*/
041/** 
042* The number of columns in the TextArea objects if
043* the window is made narrower.
044*/
045protected int colsSmaller;
046/**
047* The number of columns in the TextArea objects
048* currently.
049*/
050protected int colsCurrent;
051/**
052* The number of columns in the TextArea objects if
053* the window is made wider.
054*/
055protected int colsLarger;
056/**
057* The width of the TextArea objects in pixels when
058* they are colsSmaller columns wide.
059*/
060protected int widthSmaller;
061/**
062* The width of the TextArea objects in pixels when
063* they are colsCurrent columns wide.
064*/
065protected int widthCurrent;
066/**
067* The width of the TextArea objects in pixels when
068* they are colsLarger columns wide.
069*/
070protected int widthLarger;
071/**
072* Used to determine the number of rows that should
073* be in the TextArea objects when the height of
074* TextArea objects is made slightly smaller.
075*
076* <p>The TextArea.getMinimumSize method is
077*    applied to an object eight rows taller than
078*    the topWindow object.  This allows room for
079*    bottomWindow and the buttons.</p>
080*/
081protected int rowsSmaller;
082protected int rowsCurrent;
083protected int rowsLarger;
084/**
085* This is the height in pixels returned by the
086* TextArea.getMinimumSize method for a 
087* TextArea block rowsSmaller rows in height.
088*/
089protected int heightSmaller;
090/**
091* This is the height in pixels returned by the
092* TextArea.getMinimumSize method for a 
093* TextArea block rowsCurrent rows in height.
094*/
095protected int heightCurrent;
096/**
097* This is the height in pixels returned by the
098* TextArea.getMinimumSize method for a 
099* TextArea block rowsLarger rows in height.
100*/
101protected int heightLarger;
102/**
103* Set to true when the objects in the window
104* have been initialized.
105*
106* <p>Some events related to window movement and
107*    resizing may be generated before the objects
108*    fully initialized.  These should not be
109*    responded to as the sizes and locations have
110*    not yet been set.</p>
111*/
112protected boolean initialized = false;
113/**
114* The bottomWindow TextArea is used to hold information
115* being prepared for transmission.
116*/
117private  java.awt.TextArea bottomWindow;
118/** 
119* The topWindow TextArea holds the information sent from
120* the other end of the connection.
121*/
122private  java.awt.TextArea topWindow;
123/**
124* The sendButton Button object causes the material in the
125* lower window to be sent over the communication line.
126* <p>The data is also sent when the user depresses the Return
127*    key on the keyboard.  This button on the form may be
128*    removed at a later date.</p>
129*/
130private  java.awt.Button sendButton;
131/**
132*  The closeButton Button can be used to
133*  close the connection when it fails
134*  to close normally.
135*/
136private  java.awt.Button closeButton;
137/**
138* Object for the frame in which the application
139* is displayed.
140*
141* <p>The menu bar is attached to the frame.</p>
142*/
143private  Frame myFrame;
144/**
145* The myPanel panel provides a place to attach
146* key listeners.
147* <p>It appears that a Panel is a container, but
148*    a Frame isn't.  Since listeners must be 
149*    attached to containers, it was necessary to
150*    create a panel within the frame.</p>
151* <p>After the object is created, a listener is
152*    created with an anonymous inner class to
153*    react to changes in the size of the panel.</p>
154*/
155private  Panel myPanel;
156private  String windowName;
157/**
158* This method is called when the window is resized.
159*
160* <p>It examines the width and height of the windows
161*    to determine whether the TextArea objects should
162*    have their height and width changed.</p>
163*/
164protected void windowResizer()
165   {
166   if (!initialized) { return; }
167   if (debugLevel > 2)
168      { addMessage("Entering windowResizer"); }
169   int windowHeight = myPanel.getHeight();
170   int windowWidth = myPanel.getWidth();
171   if (debugLevel > 2)
172      { addMessage("Current panel width is "
173         .concat(Integer.toString(myPanel.getWidth()))
174         .concat("  Range: ")
175         .concat(Integer.toString(widthSmaller))
176         .concat("-")
177         .concat(Integer.toString(widthLarger))); }
178   if (windowWidth  > widthLarger + 15)
179      {
180      if (debugLevel > 0)
181         { addMessage("Expanding TextArea objects "
182           .concat(Integer.toString(colsLarger))
183           .concat(" columns")); }
184      topWindow.setColumns(colsLarger);
185      bottomWindow.setColumns(colsLarger);
186      colsCurrent = colsLarger;
187      widthCurrent = widthLarger;
188      colsSmaller = colsCurrent - 5;
189      colsLarger = colsCurrent + 5;
190      widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width;
191      widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width;
192      myPanel.repaint();
193      }
194   else if (widthCurrent + 15  > windowWidth)
195      {
196      if (debugLevel > 0)
197         { addMessage("Shrinking TextArea items "
198           .concat(Integer.toString(colsSmaller))
199           .concat(" columns")); }
200      topWindow.setColumns(colsSmaller);
201      bottomWindow.setColumns(colsSmaller);
202      colsCurrent = colsSmaller;
203      widthCurrent = widthSmaller;
204      colsSmaller = colsCurrent - 5;
205      colsLarger = colsCurrent + 5;
206      widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width;
207      widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width;
208      myPanel.repaint();
209      }
210   if (windowHeight > heightLarger + 15)
211      {
212      rowsCurrent = rowsLarger;
213      heightCurrent = heightLarger;
214      if (debugLevel > 0)
215         { addMessage("Increasing height of topWindow to "
216              .concat(Integer.toString(rowsCurrent - 8))); }
217      if (rowsCurrent > 10)
218         {
219         topWindow.setRows(rowsCurrent - 8);
220         rowsLarger = rowsCurrent + 2;
221         rowsSmaller = rowsCurrent - 2;
222         heightSmaller = topWindow.getMinimumSize(rowsSmaller, 75).height;
223         heightLarger = topWindow.getMinimumSize(rowsLarger, 75).height;
224         }
225      else
226         {
227         addMessage("Trying to reduce height below 0"
228            .concat(" while increasing height"));
229         }
230      myPanel.repaint();
231      }
232   else if (heightCurrent   > windowHeight)
233      {
234      rowsCurrent = rowsSmaller;
235      heightCurrent = heightSmaller;
236      if (debugLevel > 0)
237         { addMessage("Decreasing height of topWindow to "
238              .concat(Integer.toString(rowsCurrent - 8))); }
239      rowsLarger = rowsCurrent + 2;
240      rowsSmaller = rowsCurrent - 2;
241      if (rowsCurrent > 10)
242         {
243         topWindow.setRows(rowsCurrent - 8);
244         heightSmaller = topWindow.getMinimumSize(rowsSmaller, 75).height;
245         heightLarger = topWindow.getMinimumSize(rowsLarger, 75).height;
246         }
247      else
248         {
249         addMessage("Trying to reduce height below 0"
250            .concat(" while decreasing height"));
251         }
252      myPanel.repaint();
253      }
254   }
255/**
256* This method generates a message and adds it to the
257* log and the Sys.output character stream.
258* 
259* @param display name to be used on window
260*/
261protected void addMessage (String display)
262   {
263   if (debugLevel > 0)
264      {
265      System.out.println(display);
266      }
267   }
268/**
269* This function is called whenever a key is pressed.
270* <p>If the Delete or Backspace key is depressed, the
271*    last character in the bottomWindow textArea is
272*    removed.</p>
273* <p>If the Return key is depressed, the contents of
274*    bottomWindow are sent to the remote system and
275*    contents of bottomWindow are erased.</p>
276*    
277* @param e event to be processed
278*/
279protected void processKey (KeyEvent e)
280   {
281   char[] processed = { e.getKeyChar() };
282   if (debugLevel > 2)
283      { System.out.print(processed[0]); }
284   if (processed[0] == '\n')
285      { sendMessage(); }
286   if ((processed[0] == java.awt.event.KeyEvent.VK_BACK_SPACE) ||
287       (processed[0] == java.awt.event.KeyEvent.VK_DELETE))
288      {
289      String workingText = bottomWindow.getText();
290      bottomWindow.setText(workingText.substring(0, 
291             workingText.length() - 1));
292      }
293   else
294      { bottomWindow.append(new String(processed)); }
295   }
296/**
297* This method is called when the Send button is depressed
298* on the form
299* or the user depresses the Return key on the keyboard.
300*/
301protected void sendMessage()
302   {
303   pw.print(bottomWindow.getText().concat("\n")); 
304   pw.flush();
305   topWindow.append("Text sent: "
306         .concat(bottomWindow.getText())
307         .concat("\n")) ; 
308   bottomWindow.setText(null);
309   // bottomWindow.requestFocus();
310   }
311public communicationsWindow(String name)
312   {
313   windowName = name;
314   }
315/**
316* This method is used to control the amount of diagnostic
317* messages generated.
318* <p>A value of 0 means no messages are to be printed. Higher
319*    values generate larger amounts of messages.
320*    </p>
321*    
322* @param level value to be used for debugLevel.
323*/
324public void setDebugLevel (int level)
325   {
326   debugLevel = level;
327   }
328public int getDebugLevel ()
329   { return debugLevel; }
330/**
331* This is the main method for creating the communications
332* window and setting up the communications stream.
333* 
334* @param inputSock socket to be used for communications
335*/
336public void start (Socket inputSock)
337   {
338   if (debugLevel > 1)
339      { 
340      System.out.println
341         ("Running method start of communicationsWindow");
342      }
343   windowNumber = windowNumber + 1;
344   // thisWindow = windowNumber;
345   sock = inputSock;
346   if (debugLevel > 1)
347      { System.out.println("Point A"); }
348   myFrame = new Frame(windowName);
349   java.awt.MenuBar menubar = new java.awt.MenuBar();
350   java.awt.Menu logMenu = new java.awt.Menu("Log");
351   java.awt.MenuItem startLogging = new java.awt.MenuItem("Start logging");
352   java.awt.MenuItem stopLogging = new java.awt.MenuItem("Stop logging");
353   logMenu.add(startLogging);
354   logMenu.add(stopLogging);
355   menubar.add(logMenu);
356   myFrame.setMenuBar(menubar);
357   myPanel = new Panel();
358   startLogging.addActionListener(new ActionListener()
359      { public final void actionPerformed(ActionEvent e)
360          { 
361          System.out.println("*** Start logging ");
362          if (!logFileActive)
363             {
364             try
365                {
366                logFile = new java.io.FileWriter(logFileName, true);
367                logBuffer = new java.io.BufferedWriter(logFile);
368                logWriter = new java.io.PrintWriter(logBuffer);
369                logWriter.println( (new java.util.Date()).toString());
370                logFileActive = true;
371                }
372             catch (Exception e2)
373                {
374                System.out.println("Error when starting logging");
375                }
376             } 
377          } });
378   stopLogging.addActionListener(new ActionListener()
379      { public final void actionPerformed(ActionEvent e)
380          { 
381          System.out.println("*** Stop logging "); 
382          if (logFileActive)
383             {
384             try
385                {
386                logWriter.flush();
387                logWriter.close();
388                logFileActive = false;
389                }
390             catch (Exception e2)
391                {
392                System.out.println("Error when stopping logging");
393                }
394             }
395          } } );
396   /*
397   ** setSize is (width, height)
398   */
399   if (debugLevel > 1)
400      { System.out.println("Point B"); }
401   myFrame.setSize(800, 600);
402   layout = new FlowLayout();
403   /*
404   * Sets up the listener for keystrokes.
405   */
406   myPanel.addKeyListener(new KeyAdapter()
407      { public final void keyTyped (KeyEvent e)
408         {
409         processKey(e);
410         }  }  );
411   /*
412   ** Listen for changes to window
413   */
414   myPanel.addComponentListener(new ComponentListener()
415      {
416      public void componentHidden(ComponentEvent e)
417         {
418         if (debugLevel > 0)
419            { addMessage("Window hidden"); }
420         }
421      public void componentMoved (ComponentEvent e)
422         {
423         if (debugLevel > 0)
424            { addMessage("Window moved"); }
425         myFrame.repaint();
426         }
427      public void componentResized(ComponentEvent e)
428         {
429         if (debugLevel > 2)
430            { 
431            addMessage("Window resized "
432                 .concat(Integer.toString(myFrame.getWidth()))
433                 .concat(":")
434                 .concat(Integer.toString(myFrame.getHeight()))); 
435            }
436         windowResizer();
437         }
438      public void componentShown (ComponentEvent e)
439         {
440         if (debugLevel > 0)
441            { addMessage("Window becomes visible"); }
442         }
443      }  );
444   /* 
445   ** Specification for TextArea constructor is
446   **  (text, rows, columnns, scrollbars)
447   **
448   ** topWindow
449   */
450   topWindow = 
451      new java.awt.TextArea("", 25, 80,
452         java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY);
453   topWindow.setEditable(false);
454   topWindow.addFocusListener(new FocusAdapter()
455      { public void focusGained(FocusEvent e) 
456         {myPanel.requestFocus(); } } );
457   /*
458   ** bottomWindow
459   ** I changed from SCROLLBARS_VERTICAL_ONLY to
460   ** SCROLLBARS_BOTH
461   */
462   bottomWindow = new java.awt.TextArea("", 4, 80,
463         java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY);
464   bottomWindow.setEditable(false);
465   bottomWindow.addFocusListener(new FocusAdapter()
466      { public void focusGained(FocusEvent e) 
467         {myPanel.requestFocus(); } } );
468   /*
469   ** sendButton
470   */
471   sendButton = new Button("Send");
472   sendButton.addActionListener (new ActionListener()
473     { public final void actionPerformed (ActionEvent e)
474        {
475        sendMessage();
476        } } );
477   /*
478   ** closeButton
479   */
480   closeButton = new Button("Close");
481   closeButton.addActionListener (new ActionListener()
482     { public final void actionPerformed (ActionEvent e)
483        {try 
484            {
485            if (debugLevel > 1)
486               { System.out.println("Close button activated"); }
487            sock.close(); 
488            myFrame.removeAll();
489            return;
490            } 
491         catch (Exception e2) 
492            {
493            System.out.println("*** Start of error message");
494            System.out.println("unexpected Exception while"
495                  .concat(" processing Close button"));
496            e2.printStackTrace();
497            System.out.println("*** End of error message");
498            }
499           } } );
500   /*
501   ** Set up the various window items and prepare them
502   ** for interaction
503   */
504   myPanel.add(topWindow);
505   myPanel.add(bottomWindow);
506   myPanel.add(sendButton);
507   myPanel.add(closeButton);
508   myFrame.add(myPanel);
509   myPanel.setLayout(layout);
510   myFrame.doLayout();
511   myPanel.doLayout();
512   myFrame.setVisible(true);
513   myPanel.setVisible(true);
514   myPanel.requestFocus();
515   // String response = ""; 
516   if (debugLevel > 1)
517      { System.out.println("Creating frame"); }
518   initialized = true;
519   /*
520   ** Determine the limits for shrinking and growing
521   ** TextArea items
522   */
523   colsCurrent = 80;
524   colsSmaller = 75;
525   colsLarger = 85;
526   widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width;
527   widthCurrent = (topWindow.getMinimumSize(25, colsCurrent)).width;
528   widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width;
529   rowsCurrent = 33;
530   rowsSmaller = 31;
531   rowsLarger = 35;
532   heightSmaller = topWindow.getMinimumSize(31, 75).height;
533   heightCurrent = topWindow.getMinimumSize(33, 75).height;
534   heightLarger = topWindow.getMinimumSize(35, 75).height;
535   if (debugLevel > 0)
536      {
537      addMessage ("Next smaller: "
538         .concat(Integer.toString(colsSmaller))
539         .concat("   ")
540         .concat(Integer.toString(widthSmaller)));
541      addMessage ("Next larger: "
542         .concat(Integer.toString(colsLarger))
543         .concat("   ")
544         .concat(Integer.toString(widthLarger)));
545      addMessage("Current window width: "
546         .concat(Integer.toString(myPanel.getWidth())));
547      addMessage("Trigger heights are "
548         .concat(Integer.toString(heightSmaller)).concat(" ")
549         .concat(Integer.toString(heightCurrent)).concat(" ")
550         .concat(Integer.toString(heightLarger)));
551      }
552
553   try 
554      { 
555       if (System.getProperty("LOCAL_HOST") == null) 
556          { System.setProperty("LOCAL_HOST", 
557                   sock.getLocalAddress().getHostName());} 
558       pw = new PrintWriter(sock.getOutputStream(),
559               true); 
560       streamReader = new InputStreamReader(sock.getInputStream(),
561          "8859_1");
562       in = new BufferedReader(streamReader); 
563       // Socket now ready for data transfer 
564       /*
565       ** The problem is how to make the application close
566       ** when the socket is closed
567       **
568       ** The method readLine waits until information is 
569       ** available.  However, if the connection has been
570       ** closed, it returns a null value.
571       */
572       while (true)
573          {
574          if (inputSock.isClosed())
575             { break; }
576          String request = in.readLine(); 
577          if (request == null)
578             {break;}
579          topWindow.append(request.concat("\n"));
580          if (logFileActive)
581             { logWriter.println(request); }
582          }
583       if (logFileActive)
584          {
585          logWriter.flush();
586          logWriter.close();
587          } 
588       } 
589   catch (SocketException e)
590       {
591       System.out.println("*** Start of error message");
592       System.out.println("SocketException while reading data");
593       System.out.println("Message: ".concat(e.getMessage()));
594       System.out.println("Can be ignored if message is *Socket closed*"
595             .concat(" when socket is closed"));
596       e.printStackTrace();
597       System.out.println("*** End of error message");
598       return;
599       } 
600   catch (InterruptedIOException e) 
601       {
602       System.out.println("*** Start of error message");
603       System.out.println("InterruptedIOException while reading data");
604       System.out.println("Message: ".concat(e.getMessage()));
605       e.printStackTrace(); 
606       System.out.println("*** End of error message");} 
607   catch (Exception e) 
608       {
609       System.out.println("*** Start of error message");
610       System.out.println("unexpected Exception while reading data");
611       System.out.println("Message: ".concat(e.getMessage()));
612       e.printStackTrace(); 
613       } 
614   // Test if socket still open 
615   if (debugLevel > 1)
616      { System.out.println("Removing frame"); }
617   myFrame.removeAll();
618   myFrame.removeNotify();
619   try 
620      {if (! sock.isClosed()) 
621          {sock.close(); } 
622      } 
623   catch (Exception e) 
624      {
625      System.out.println("*** Start of error message"); 
626      System.out.println("Error when closing socket");
627      System.out.println("Message: ".concat(e.getMessage()));
628      e.printStackTrace();
629      System.out.println("*** End of error message");
630      } 
631    return;
632   }  // end of  method
633} // end of class