| We have seen that some stream classes represent endpoints: the 
              sources and destinations of data. For example a file can be a source 
              or destination via the FileInputStream 
              and FileOutputStream 
              classes. Other stream classes, such as buffer wrappers, modify, 
              handle, or monitor the data in some way as it passes through.  In this demo we create classes that allow histograms to become 
              the destination for a stream. In the next section we will look at 
              stream classes that filter the histogram data as it heads for its 
              destination. The demonstration applet below illustrates how we can use the stream 
              framework not only to grab data from a disk file or send text to 
              a console but to move data within a program in a systematic, consistent 
              manner.  In the program HistStreamApplet 
              shown below, simulated data is sent to a histogram via an output 
              stream provided by a StreamedHistPanel, 
              which is a subclass of our HistPanel 
              component class. (See source code listings below.) The StreamedHistPanel 
              includes an inner class called HistogramOutputStream 
              that extends OutputStream. 
              The class HistogramOutputStream 
              overrides the write(int 
              b) method in OutputStream 
              with a method that adds data to the histogram in the StreamedHistPanel 
              object (it can access the histogram since it is an inner class of 
              StreamedHistPanel) 
              .  For HistogramOutputStream, 
              a histogram becomes the destination for the stream just as an array 
              is the destination for ByteArrayInputStream 
              or a string is the destination for StringWriter. 
             The applet, derived from the TimerHistFillApplet.java 
              example in Chapter 8: Tech : 
              Timers, simulates readings from three sensors using MakeSensorData 
              and streams the data to three corresponding StreamedHistPanel 
              components on the interface. The sensor readings include differing 
              offsets (called pedestals) and response sensitivities (slopes) as 
              is typical with real hardware.  Note that in our HistogramOutputStream 
              we use the full four bytes of the integer argument in write(int 
              b). In the normal use of this method in OutputStream 
              subclasses, only the lowest order byte would be written. If we wanted 
              to stream data from MakeSensorData 
              to other streams, such as to a FileOutputStream, 
              we would need to pack the sensor reading in only the lower order 
              byte (and thus lose precision), or into a byte array as we did in 
              the previous example.  In a similar way, you could make a histogram or histogram panel 
              into a source stream by creating an InputStream 
              subclass that reads data from the histogram.   We note that StreamedHistPanel 
              follows the example of streaming to a TextArea 
              given in Harold, 1999. 
             
              
                 
                  |  
                       
                        HistStreamApplet.java 
                          - 
                          This applet/application uses StreamedHistPanel objects 
                          to display the contents of 3 Histograms that simulate 
                          distributions of sensor data. The basic form of the 
                          class goes as TimerHistFillApplet in Chapter 
                          8: Tech. However, data is sent to the StreamedHistPanels 
                          as OutputStreams.
 
 Data is generated from an instance of MakeSensorData 
                          in a thread to simulate the input of data events at 
                          random times. The three sensors each produce a Gaussian 
                          distribution of values but they are transformed by different 
                          slopes and offsets (or pedestals) to represent variations 
                          in the sensor hardware.
  
                       
                        + New classes:StreamedHistPanel.java 
                        -  
                        This class extends HistPanel. It provides a HistPanel 
                        destination for an output stream. It uses an instance 
                        of HistPanel to display the data and to hold the data 
                        values in an array.
 It 
                        uses an innter class called HistogramOutputStream 
                        that extends OutputStream. An instance of this stream 
                        object is provided with the getOutputStream() method. 
                        Data written to theo HistogramOutputStream is added to 
                        the histogram that is displayed on the panel. Follows 
                        similar pattern as the StreamedTextArea class in "Java 
                        IO" by E.R.Harold. MakeSensorData.java 
                        - 
                        Runnable class that generates a Guassian distributed random 
                        data and write it to the HistogramOutputStream for display 
                        on the histogram. + 
                        Previous classes: Chapter 
                        6:Tech: Histogram.java, 
                        HistPanel.java
 Chapter 
                        6:Tech: PlotPanel.java, 
                        PlotFormat.java
 
 |   
                  | import 
                    javax.swing.*; import java.awt.*;
 import java.awt.event.*;
 import java.io.*;
 
 /**
 * This applet/application uses StreamedHistPanels 
                    to display the contents
 * of 3 Histograms to show distributions of sensor 
                    data. The basic form
 * of the class goes as TimerHistFill_JApplet11. 
                    However, data is sent to
 * the StreamedHistPanels as OutputStreams.
 *
 * Data is generated from an instance of MakeSensorData 
                    in a thread
 * to simulate the input of data events at random 
                    times. The three
 * sensors each produce a Gaussian distribution 
                    of values but they
 * are transformed by different slopes and offsets  (or 
                    pedestals)
 * to represent variations in the sensor hardware.
 **/
 public class HistStreamApplet extends JApplet
 implements ActionListener, Updateable
 {
 final static int NUM_HISTS = 3;
 
 // Histograms for the panels.
 Histogram [] fHistogram = new Histogram[NUM_HISTS];
 
 // The HistPanel subclass that accept data streams.
 StreamedHistPanel [] fHistPanel = new StreamedHistPanel[NUM_HISTS];
 
 // Create a panel to hold the histogram sub-panels
 JPanel fHistsPanel = null;
 
 // Array of treams to the panels
 OutputStream [] fDataOut = new OutputStream[NUM_HISTS];
 
 // Constants for three channels of sensor data.
 double [] fSlope    = {0.6, 
                    1.2, 1.1};
 double [] fPedestal = {2.4, 1.3, 3.4};
 
 int fNumDataPoints = 100;
 
 boolean fMakingHist = false;
 boolean fUpdateDisplay = false;
 MakeSensorData fMakeData = null;
 
 // Use the java.util Timer and TimerTask combo
 // for timing events.
 java.util.Timer fTimer = null;
 
 // A text field for input strings
 JTextField fTextField = null;
 
 // Flag for whether the applet is in a browser
 // or running via the main () below.
 boolean fInBrowser=true;
 
 //Buttons
 JButton fGoButton    = null;
 JButton fClearButton = null;
 JButton fExitButton  = null;
 
 public void init () {
 Container content_pane = getContentPane 
                    ();
 JPanel panel = new JPanel (new BorderLayout 
                    ());
 
 // Use a textfield for an input parameter.
 fTextField =
 new JTextField (Integer.toString 
                    (fNumDataPoints), 10);
 
 // If return hit after entering text, 
                    the
 // actionPerformed will be invoked.
 fTextField.addActionListener (this);
 
 fGoButton = new JButton ("Go");
 fGoButton.addActionListener (this);
 
 fClearButton = new JButton ("Clear");
 fClearButton.addActionListener (this);
 
 fExitButton = new JButton ("Exit");
 fExitButton.addActionListener (this);
 
 JPanel control_panel = new JPanel 
                    ();
 
 control_panel.add (fTextField);
 control_panel.add (fGoButton);
 control_panel.add (fClearButton);
 control_panel.add (fExitButton);
 
 // Create a panel to hold the histogram 
                    sub-panels
 fHistsPanel = new JPanel (new GridLayout 
                    (1,3));
 
 // Create the HistPanels and their 
                    histograms.
 for (int i=0; i < NUM_HISTS; i++) 
                    {
 fHistogram[i] 
                    = new Histogram ("Sensor "+i,
 "Data", 
                    25,0.0,10.0);
 fHistPanel[i] 
                    = new StreamedHistPanel (fHistogram[i]);
 
 // Add the 
                    histogram panels to the container panel
 fHistsPanel.add 
                    (fHistPanel[i]);
 
 // Get the 
                    output streams for each panel.
 fDataOut[i] 
                    = fHistPanel[i].getOutputStream ();
 }
 makeHist ();
 fGoButton.setText ("Stop");
 fClearButton.setEnabled (false);
 if (fInBrowser) fExitButton.setEnabled 
                    (false);
 
 panel.add (fHistsPanel,"Center");
 panel.add (control_panel,"South");
 
 // Add text area with scrolling to 
                    the contentPane.
 content_pane.add (panel);
 
 } // init
 
 public void actionPerformed (ActionEvent e) {
 Object source = e.getSource ();
 if (source == fGoButton || source 
                    == fTextField) {
 String str_num_data_points 
                    = fTextField.getText ();
 try {
 fNumDataPoints 
                    = Integer.parseInt (str_num_data_points);
 } catch (NumberFormatException 
                    ex) {
 // Could open 
                    an error dialog here but just
 // display 
                    a message on the browser status line.
 showStatus 
                    ("Bad input value");
 return;
 }
 if (!fMakingHist) {
 makeHist ();
 fGoButton.setText 
                    ("Stop");
 fClearButton.setEnabled 
                    (false);
 } else { // Stop button 
                    has been pushed
 fGoButton.setText 
                    ("Go");
 fClearButton.setEnabled 
                    (true);
 fMakingHist 
                    = false;
 }
 } else if (source == fClearButton 
                    ) {
 for (int i=0; 
                    i < NUM_HISTS; i++)
 {   
                    fHistogram[i].clear ();}
 repaint ();
 }else if (!fInBrowser)
 System.exit 
                    (0);
 
 } // actionPerformed
 
 /** Create the histograms and start the timers. 
                    **/
 void makeHist () {
 // Only allow for one data maker at 
                    a time.
 if (fMakingHist) return;
 fMakingHist = true;
 
 // Create the runnable object to fill 
                    the histograms
 // The data will simualte sensors 
                    that return data with
 // slightly different slopes and pedestals.
 // The maximum delay between data 
                    readings will be 500msecs.
 fMakeData =
 new MakeSensorData (this, 
                    fDataOut, fNumDataPoints,
 fSlope, 
                    fPedestal, 500);
 Thread data_thread = new Thread (fMakeData);
 
 // Before starting the filling, create 
                    the timer task
 // that will cause the histogram display 
                    to update
 // during the filling.
 // Create a timer. TimerTask created 
                    in MakeHist ()
 fTimer = new java.util.Timer ();
 
 // Start the timer after 100ms and 
                    then repeat calls
 // to run in PaintHistTask object 
                    every 250ms.
 fTimer.schedule (new PaintHistTask 
                    (), 100, 250);
 
 // Now start the data filling.
 data_thread.start ();
 return;
 
 } // makeHist
 
 /**
 * Use the inner class technique to 
                    define the
 * TimerTask subclass for signalling 
                    that the display
 * should be updated.
 **/
 class PaintHistTask extends java.util.TimerTask 
                    {
 public void run () {
 fUpdateDisplay = true;
 }
 } // class PaintHistTask
 
 /**
 *  Repaint the histogram 
                    display and turn off the
 *  update display flag. 
                    Return the makingHist flag
 *  to indicate if updating 
                    should continue.
 **/
 public boolean update (Object obj) {
 // Don't update the display until 
                    the timer
 // turns on the updateDisplay flag.
 if ( fUpdateDisplay) {
 // Possible this method 
                    called before outputPanel
 // created in the init 
                    (). So check if it exists
 // before attempting to 
                    repaint.
 if (fHistsPanel != null) 
                    fHistsPanel.repaint ();
 fUpdateDisplay = false;
 }
 return fMakingHist;
 
 } // update
 
 /** Called when the histogram filling is finished. 
                    **/
 public void done () {
 fMakingHist = false;
 
 // Stop the histogram display updates.
 fTimer.cancel ();
 
 // Update one last time
 fHistsPanel.repaint ();
 
 // Reset the buttons.
 fGoButton.setText ("Go");
 fClearButton.setEnabled (true);
 
 } // done
 
 public static void main (String[] args) {
 int frame_width=450;
 int frame_height=300;
 
 // Create applet and add to frame.
 HistStreamApplet applet = new HistStreamApplet 
                    ();
 applet.fInBrowser = false;
 applet.init ();
 
 // Following anonymous class used 
                    to close window & exit program
 JFrame f = new JFrame ("Demo");
 f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
 
 // Add applet to the frame
 f.getContentPane ().add ( applet);
 f.setSize (new Dimension (frame_width,frame_height));
 f.setVisible (true);
 
 } // main
 
 } // class HistStreamApplet
 |  
                  | import 
                    java.io.*; 
 /**
 *  This class provides provides a HistPanel 
                    destination for an
 *  output stream. It uses an instance 
                    of HistPanel to
 *  display the data and to hold the 
                    data values in an array.
 *
 *  Follows similar pattern as the StreamedTextArea 
                    class in
 *  "Java IO" by E.R.Harold.
 **/
 public class StreamedHistPanel extends HistPanel
 {
 OutputStream fHistStream = null;
 boolean fNewDataFlag = false;
 
 /** Create the panel with the histogram. **/
 public StreamedHistPanel (Histogram histogram) 
                    {
 super (histogram);
 fHistStream = new HistogramOutputStream 
                    ();
 } // ctor
 
 public OutputStream getOutputStream () {
 return fHistStream;
 }
 
 /**
 *  This internal class provides 
                    the stream to write data.
 *  to the histogram destination. 
                    The class extends the
 *  OutputStream so as to 
                    use the double and float type
 *  write methods. When data 
                    is written to the stream, a
 *  flag indicates that new 
                    data has arrived in case the
 *  GUI wants to update the 
                    display.
 *
 
 
 *  For out needs here, we 
                    only override the write ()
 *  and writeFloat () methods.
 **/
 class HistogramOutputStream extends OutputStream 
                    {
 /**
 * In OutputStream write (int b), 
                    only the lower byte
 * of the int value is used but here 
                    we use the full
 * value
 */
 public synchronized void write (int 
                    data) {
 // Convert back to double 
                    and shift down two decimal
 // places.
 fHistogram.add (((double)data)/100.0);
 fNewDataFlag = true;
 }
 } // class HistogramOutputStream
 
 } // class StreamedHistPanel
 |   
                  | import 
                    java.io.*; 
 /**  This runnable class fills the histogram. **/
 public class MakeSensorData implements Runnable
 {
 OutputStream [] fDataOut;
 int fNumData;
 Updateable fOwner;
 
 // Maximum delay between data points with default
 int fMaxDelay = 1000;
 
 // Create an instance of the Random class for
 // producing our random values.
 static java.util.Random fRan = new java.util.Random 
                    ();
 
 // Constants for three channels of sensor data.
 double [] fSlope ;
 double [] fPedestal;
 
 /**
 *  Constructor
 *  @param fOwner to call back with 
                    updates.
 *  @param fDataOut stream destinations 
                    for the data.
 *  @param fNumData Number of data readings.
 *  @param fMaxDelay maximum delay in 
                    msecs between data readings.
 **/
 public MakeSensorData (Updateable owner,
 OutputStream [] dataOut, int numData,
 double [] slope, double [] pedestal,
 int maxDelay) {
 
 fDataOut  = dataOut;
 fSlope    = slope;
 fPedestal = pedestal;
 
 fNumData  = numData;
 fOwner = fOwner;
 
 // Maximum delay time between new 
                    data points
 fMaxDelay = maxDelay;
 
 } // ctor
 
 /**
 * Simulate data taking by generating 
                    Gaussian distributions
 * for three data channels, each with 
                    their own fPedestal and
 * fSlope.
 **/
 public void run () {
 
 for (int i=0; i < fNumData; i++) {
 for (int j=0; j < fDataOut.length; 
                    j++) {
 // Create some data. 
                    Make sure it is a positive value.
 double val = -1.0;
 // Signal events
 while (val < 0.0)
 val = fRan.nextGaussian 
                    () + 4.0;
 
 // Simulate variations 
                    in sensor fSlope and offsets
 val = fSlope[j] * val 
                    + fPedestal[j];
 
 // Shift up two decimal 
                    places before converting
 // to integer.
 int ival =  (int) 
                    (100 * val);
 try {
 // Send data 
                    to stream destination
 fDataOut[j].write 
                    (ival);
 }
 catch (IOException ioe)
 {}
 }
 
 // Update the fOwner. Stop updates 
                    if
 // false returned.
 if (!fOwner.update (this)) break;
 
 // Pause for random periods between 
                    events
 try {
 Thread.sleep (fRan.nextInt 
                    (fMaxDelay));
 }
 catch (InterruptedException e)
 {}
 }
 // Tell the fOwner that the data has been sent.
 fOwner.done ();
 } // run
 
 } // class MakeSensorData
 |    References & Web 
              Resources Latest update: Nov. 14, 2004 |