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*/
259protected void addMessage (String display)
260   {
261   if (debugLevel > 0)
262      {
263      System.out.println(display);
264      }
265   }
266/**
267* This function is called whenever a key is pressed.
268* <p>If the Delete or Backspace key is depressed, the
269*    last character in the bottomWindow textArea is
270*    removed.</p>
271* <p>If the Return key is depressed, the contents of
272*    bottomWindow are sent to the remote system and
273*    contents of bottomWindow are erased.</p>
274*/
275protected void processKey (KeyEvent e)
276   {
277   char[] processed = { e.getKeyChar() };
278   if (debugLevel > 2)
279      { System.out.print(processed[0]); }
280   if (processed[0] == '\n')
281      { sendMessage(); }
282   if ((processed[0] == java.awt.event.KeyEvent.VK_BACK_SPACE) ||
283       (processed[0] == java.awt.event.KeyEvent.VK_DELETE))
284      {
285      String workingText = bottomWindow.getText();
286      bottomWindow.setText(workingText.substring(0, 
287             workingText.length() - 1));
288      }
289   else
290      { bottomWindow.append(new String(processed)); }
291   }
292/**
293* This method is called when the Send button is depressed
294* on the form
295* or the user depresses the Return key on the keyboard.
296*/
297protected void sendMessage()
298   {
299   pw.print(bottomWindow.getText().concat("\n")); 
300   pw.flush();
301   topWindow.append("Text sent: "
302         .concat(bottomWindow.getText())
303         .concat("\n")) ; 
304   bottomWindow.setText(null);
305   // bottomWindow.requestFocus();
306   }
307public communicationsWindow(String name)
308   {
309   windowName = name;
310   }
311/**
312* This method is used to control the amount of diagnostic
313* messages generated.
314* <p>A value of 0 means no messages are to be printed. Higher
315*    values generate larger amounts of messages.
316*    </p>
317*/
318public void setDebugLevel (int level)
319   {
320   debugLevel = level;
321   }
322public int getDebugLevel ()
323   { return debugLevel; }
324/**
325* This is the main method for creating the communications
326* window and setting up the communications stream.
327*/
328public void start (Socket inputSock)
329   {
330   if (debugLevel > 1)
331      { 
332      System.out.println
333         ("Running method start of communicationsWindow");
334      }
335   windowNumber = windowNumber + 1;
336   // thisWindow = windowNumber;
337   sock = inputSock;
338   if (debugLevel > 1)
339      { System.out.println("Point A"); }
340   myFrame = new Frame(windowName);
341   java.awt.MenuBar menubar = new java.awt.MenuBar();
342   java.awt.Menu logMenu = new java.awt.Menu("Log");
343   java.awt.MenuItem startLogging = new java.awt.MenuItem("Start logging");
344   java.awt.MenuItem stopLogging = new java.awt.MenuItem("Stop logging");
345   logMenu.add(startLogging);
346   logMenu.add(stopLogging);
347   menubar.add(logMenu);
348   myFrame.setMenuBar(menubar);
349   myPanel = new Panel();
350   startLogging.addActionListener(new ActionListener()
351      { public final void actionPerformed(ActionEvent e)
352          { 
353          System.out.println("*** Start logging ");
354          if (!logFileActive)
355             {
356             try
357                {
358                logFile = new java.io.FileWriter(logFileName, true);
359                logBuffer = new java.io.BufferedWriter(logFile);
360                logWriter = new java.io.PrintWriter(logBuffer);
361                logWriter.println( (new java.util.Date()).toString());
362                logFileActive = true;
363                }
364             catch (Exception e2)
365                {
366                System.out.println("Error when starting logging");
367                }
368             } 
369          } });
370   stopLogging.addActionListener(new ActionListener()
371      { public final void actionPerformed(ActionEvent e)
372          { 
373          System.out.println("*** Stop logging "); 
374          if (logFileActive)
375             {
376             try
377                {
378                logWriter.flush();
379                logWriter.close();
380                logFileActive = false;
381                }
382             catch (Exception e2)
383                {
384                System.out.println("Error when stopping logging");
385                }
386             }
387          } } );
388   /*
389   ** setSize is (width, height)
390   */
391   if (debugLevel > 1)
392      { System.out.println("Point B"); }
393   myFrame.setSize(800, 600);
394   layout = new FlowLayout();
395   /*
396   * Sets up the listener for keystrokes.
397   */
398   myPanel.addKeyListener(new KeyAdapter()
399      { public final void keyTyped (KeyEvent e)
400         {
401         processKey(e);
402         }  }  );
403   /*
404   ** Listen for changes to window
405   */
406   myPanel.addComponentListener(new ComponentListener()
407      {
408      public void componentHidden(ComponentEvent e)
409         {
410         if (debugLevel > 0)
411            { addMessage("Window hidden"); }
412         }
413      public void componentMoved (ComponentEvent e)
414         {
415         if (debugLevel > 0)
416            { addMessage("Window moved"); }
417         myFrame.repaint();
418         }
419      public void componentResized(ComponentEvent e)
420         {
421         if (debugLevel > 2)
422            { 
423            addMessage("Window resized "
424                 .concat(Integer.toString(myFrame.getWidth()))
425                 .concat(":")
426                 .concat(Integer.toString(myFrame.getHeight()))); 
427            }
428         windowResizer();
429         }
430      public void componentShown (ComponentEvent e)
431         {
432         if (debugLevel > 0)
433            { addMessage("Window becomes visible"); }
434         }
435      }  );
436   /* 
437   ** Specification for TextArea constructor is
438   **  (text, rows, columnns, scrollbars)
439   **
440   ** topWindow
441   */
442   topWindow = 
443      new java.awt.TextArea("", 25, 80,
444         java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY);
445   topWindow.setEditable(false);
446   topWindow.addFocusListener(new FocusAdapter()
447      { public void focusGained(FocusEvent e) 
448         {myPanel.requestFocus(); } } );
449   /*
450   ** bottomWindow
451   ** I changed from SCROLLBARS_VERTICAL_ONLY to
452   ** SCROLLBARS_BOTH
453   */
454   bottomWindow = new java.awt.TextArea("", 4, 80,
455         java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY);
456   bottomWindow.setEditable(false);
457   bottomWindow.addFocusListener(new FocusAdapter()
458      { public void focusGained(FocusEvent e) 
459         {myPanel.requestFocus(); } } );
460   /*
461   ** sendButton
462   */
463   sendButton = new Button("Send");
464   sendButton.addActionListener (new ActionListener()
465     { public final void actionPerformed (ActionEvent e)
466        {
467        sendMessage();
468        } } );
469   /*
470   ** closeButton
471   */
472   closeButton = new Button("Close");
473   closeButton.addActionListener (new ActionListener()
474     { public final void actionPerformed (ActionEvent e)
475        {try 
476            {
477            if (debugLevel > 1)
478               { System.out.println("Close button activated"); }
479            sock.close(); 
480            myFrame.removeAll();
481            return;
482            } 
483         catch (Exception e2) 
484            {
485            System.out.println("*** Start of error message");
486            System.out.println("unexpected Exception while"
487                  .concat(" processing Close button"));
488            e2.printStackTrace();
489            System.out.println("*** End of error message");
490            }
491           } } );
492   /*
493   ** Set up the various window items and prepare them
494   ** for interaction
495   */
496   myPanel.add(topWindow);
497   myPanel.add(bottomWindow);
498   myPanel.add(sendButton);
499   myPanel.add(closeButton);
500   myFrame.add(myPanel);
501   myPanel.setLayout(layout);
502   myFrame.doLayout();
503   myPanel.doLayout();
504   myFrame.setVisible(true);
505   myPanel.setVisible(true);
506   myPanel.requestFocus();
507   // String response = ""; 
508   if (debugLevel > 1)
509      { System.out.println("Creating frame"); }
510   initialized = true;
511   /*
512   ** Determine the limits for shrinking and growing
513   ** TextArea items
514   */
515   colsCurrent = 80;
516   colsSmaller = 75;
517   colsLarger = 85;
518   widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width;
519   widthCurrent = (topWindow.getMinimumSize(25, colsCurrent)).width;
520   widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width;
521   rowsCurrent = 33;
522   rowsSmaller = 31;
523   rowsLarger = 35;
524   heightSmaller = topWindow.getMinimumSize(31, 75).height;
525   heightCurrent = topWindow.getMinimumSize(33, 75).height;
526   heightLarger = topWindow.getMinimumSize(35, 75).height;
527   if (debugLevel > 0)
528      {
529      addMessage ("Next smaller: "
530         .concat(Integer.toString(colsSmaller))
531         .concat("   ")
532         .concat(Integer.toString(widthSmaller)));
533      addMessage ("Next larger: "
534         .concat(Integer.toString(colsLarger))
535         .concat("   ")
536         .concat(Integer.toString(widthLarger)));
537      addMessage("Current window width: "
538         .concat(Integer.toString(myPanel.getWidth())));
539      addMessage("Trigger heights are "
540         .concat(Integer.toString(heightSmaller)).concat(" ")
541         .concat(Integer.toString(heightCurrent)).concat(" ")
542         .concat(Integer.toString(heightLarger)));
543      }
544
545   try 
546      { 
547       if (System.getProperty("LOCAL_HOST") == null) 
548          { System.setProperty("LOCAL_HOST", 
549                   sock.getLocalAddress().getHostName());} 
550       pw = new PrintWriter(sock.getOutputStream(),
551               true); 
552       streamReader = new InputStreamReader(sock.getInputStream(),
553          "8859_1");
554       in = new BufferedReader(streamReader); 
555       // Socket now ready for data transfer 
556       /*
557       ** The problem is how to make the application close
558       ** when the socket is closed
559       **
560       ** The method readLine waits until information is 
561       ** available.  However, if the connection has been
562       ** closed, it returns a null value.
563       */
564       while (true)
565          {
566          if (inputSock.isClosed())
567             { break; }
568          String request = in.readLine(); 
569          if (request == null)
570             {break;}
571          topWindow.append(request.concat("\n"));
572          if (logFileActive)
573             { logWriter.println(request); }
574          }
575       if (logFileActive)
576          {
577          logWriter.flush();
578          logWriter.close();
579          } 
580       } 
581   catch (SocketException e)
582       {
583       System.out.println("*** Start of error message");
584       System.out.println("SocketException while reading data");
585       System.out.println("Message: ".concat(e.getMessage()));
586       System.out.println("Can be ignored if message is *Socket closed*"
587             .concat(" when socket is closed"));
588       e.printStackTrace();
589       System.out.println("*** End of error message");
590       return;
591       } 
592   catch (InterruptedIOException e) 
593       {
594       System.out.println("*** Start of error message");
595       System.out.println("InterruptedIOException while reading data");
596       System.out.println("Message: ".concat(e.getMessage()));
597       e.printStackTrace(); 
598       System.out.println("*** End of error message");} 
599   catch (Exception e) 
600       {
601       System.out.println("*** Start of error message");
602       System.out.println("unexpected Exception while reading data");
603       System.out.println("Message: ".concat(e.getMessage()));
604       e.printStackTrace(); 
605       } 
606   // Test if socket still open 
607   if (debugLevel > 1)
608      { System.out.println("Removing frame"); }
609   myFrame.removeAll();
610   myFrame.removeNotify();
611   try 
612      {if (! sock.isClosed()) 
613          {sock.close(); } 
614      } 
615   catch (Exception e) 
616      {
617      System.out.println("*** Start of error message"); 
618      System.out.println("Error when closing socket");
619      System.out.println("Message: ".concat(e.getMessage()));
620      e.printStackTrace();
621      System.out.println("*** End of error message");
622      } 
623    return;
624   }  // end of  method
625} // end of class