Validated by Amaya

INEW 2338, Advanced Java Study Guide:  Multimedia

Published November 8, 2004
Revised January 9, 2010 for Amays compatibility
By Richard G. Baldwin

File: Inew2338Sg008.htm


Welcome

The purpose of this series of tutorial lessons is to help you learn the essential topics covered in INEW 2338, Advanced Java.

These lessons provide questions, answers, and explanations designed to help you to understand the essential Java features covered by the Advanced Java course.

The textbook for this course is Advanced Java Internet Applications, Second Edition by Art Gittleman, ISBN 1-57676-096-0.  This study guide is for Chapter 8 in the textbook.

In addition to the textbook, I recommend that you study my extensive collection of online Java tutorial lessons.  Those tutorial lessons are published at http://www.dickbaldwin.com.

For this particular study guide, you should study lessons 1450 through 1470 along with lessons 2004 through 2026 at the URL given above.


Questions

1.  The following five boxes contain a Java animation program, three gif images showing a simple stick man in three different positions, and a screen shot of the program output.  Assume that the three gif image files are in the same directory as the compiled class file for the program.  (The program can access them simply by specifying their file names.)

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Inew2338_130 extends JFrame{
  Thread animate;//Store ref to animation thread
  ImageIcon images[] = {//An array of images
          new ImageIcon("inew2338sg008a.gif"),
    new ImageIcon("inew2338sg008b.gif"),
    new ImageIcon("inew2338sg008c.gif")};
  JLabel label = new JLabel(images[0]);

  //-------------------------------------------//
  public Inew2338_130(){//constructor

    getContentPane().add(label);

    //Use an anonymous inner class to register a
    // mouse listener
    getContentPane().addMouseListener(
      new MouseAdapter(){
        public void mouseEntered(MouseEvent e){
          //Get a new animation thread and start
          // the animation on it.
          animate = new Animate();
          animate.start();
        }//end mouseEntered

        public void mouseExited(MouseEvent e){
          animate.interrupt();
          while (animate.isAlive()){}//loop;
          animate = null;
          label.setIcon(images[0]);
          label.repaint();
        }//end MouseExited
      }//end new MouseAdapter
    );//end addMouseListener()
    //End definition of anonymous inner class

    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setTitle("Copyright 2003, R.G.Baldwin");
    setSize(250,200);
    setVisible(true);

  }//end constructor
  //-------------------------------------------//

  public static void main(String[] args){
    new Inew2338_130();
  }//end main
  //-------------------------------------------//

  //Ordinary inner class to animate the image
  class Animate extends Thread{

    public void run(){//begin run method
      try{
              //The following code will continue to
              // loop until the animation thread is
              // interrupted.
        while(true){
          //Display several images in succession.
          display(1,500);
          display(0,500);
          display(2,500);
          display(0,500);
        }//end while loop
      }catch(Exception ex){
        if(ex instanceof InterruptedException){
          //Do nothing. This exception is
          // expected.
        }else{//Unexpected exception occurred.
          System.out.println(ex);
          System.exit(1);//terminate program
        }//end else
      }//end catch
    }//end run
    //-----------------------------------------//

    //This method displays an image and sleeps
    // for a prescribed period of time.  It
    // terminates and throws an
    // InterruptedException when interrupted.
    void display(int image,int delay)
                    throws InterruptedException{
      //Select and display an image.
      label.setIcon(images[image]);
      label.repaint();
      //Check interrupt status.  If interrupted
      // while not asleep, force animation to
      // terminate.
      if(Thread.currentThread().interrupted()){
        throw(new InterruptedException());
      }//end if
      //Delay specified number of msec.
      Thread.currentThread().sleep(delay);
    }//end display method
    //-----------------------------------------//
  }//end inner class named Animate

}//end class Inew2338_130


inew2338sg008a.gif


inew2338sg008b.gif


inew2338sg008c.gif


Runtime screen shot

True or false?  The stick man starts dancing by cycling through the gif images when the program starts running, .  The stick man continues dancing until the user terminates the program.

Answer and Explanation

2.  The following three boxes contain an animation program and seven gif images.  One of the images is a background image of a starfish in an aquarium.  The other six images are pictures of colored balls, which are used for animation.

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Inew2338_132 extends Frame
                            implements Runnable {
  private Image offScreenImage;
  private Image backGroundImage;
  private Image[] gifImages = new Image[6];
  //Offscreen graphics context
  private Graphics offScreenGraphicsCtx;
  private Thread animationThread;
  private MediaTracker mediaTracker;
  private SpriteManager spriteManager;
  private int animationDelay = 83;//12 frames/sec
  private Random rand = new Random(System.
                            currentTimeMillis());

  public static void main(
                        String[] args){
    new Inew2338_132();
  }//end main
  //-------------------------------------------//

  Inew2338_132() {//constructor
    // Load and track the images
    mediaTracker = new MediaTracker(this);
    //Get and track the background image
    backGroundImage =
                Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008e.gif");
    mediaTracker.addImage(backGroundImage, 0);

    //Get and track 6 images to use for sprites
    gifImages[0] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008f.gif");
    mediaTracker.addImage(gifImages[0], 0);

    gifImages[1] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008g.gif");
    mediaTracker.addImage(gifImages[1], 0);

    gifImages[2] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008h.gif");
    mediaTracker.addImage(gifImages[2], 0);

    gifImages[3] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008i.gif");
    mediaTracker.addImage(gifImages[3], 0);

    gifImages[4] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008j.gif");
    mediaTracker.addImage(gifImages[4], 0);

    gifImages[5] = Toolkit.getDefaultToolkit().
                  getImage("inew2338sg008k.gif");
    mediaTracker.addImage(gifImages[5], 0);

    //Block and wait for all images to be loaded
    try {
      mediaTracker.waitForID(0);
    }catch (InterruptedException e) {
      System.out.println(e);
    }//end catch

    //Base the Frame size on the size of the
    // background image.
    //These getter methods return -1 if the size
    // is not yet known.
    //Insets will be used later to limit the
    // graphics area to the client area of the
    // Frame.
    int width = -1;
    int height = -1;
    while(width == -1 || height == -1){
      System.out.println("Waiting for image");
      width = backGroundImage.getWidth(this);
      height = backGroundImage.getHeight(this);
    }//end while loop

    //Display the frame
    setSize(width,height);
    setTitle("Copyright 2001, R.G.Baldwin");
    setVisible(true);

    //Create and start animation thread
    animationThread = new Thread(this);
    animationThread.start();

    //Anonymous inner class window listener to
    // terminate the program.
    this.addWindowListener(new WindowAdapter(){
      public void windowClosing(WindowEvent e){
        System.exit(0);}});

  }//end constructor
  //-------------------------------------------//

  public void run() {
    //Create and add sprites to the sprite
    // manager
    spriteManager = new SpriteManager(
                       new BackgroundImage(
                         this, backGroundImage));
    //Create 15 sprites from 6 gif files.
    for (int cnt = 0; cnt < 15; cnt++){
      Point position = spriteManager.
                  getEmptyPosition(new Dimension(
                     gifImages[0].getWidth(this),
                     gifImages[0].
                     getHeight(this)));
      spriteManager.addSprite(
                  makeSprite(position, cnt % 6));
    }//end for loop

    //Loop, sleep, and update sprite positions
    // once each 83 milliseconds
    long time = System.currentTimeMillis();
    while (true) {//infinite loop
      spriteManager.update();
      repaint();
      try {
        time += animationDelay;
        Thread.sleep(Math.max(0,
             time - System.currentTimeMillis()));
      }catch (InterruptedException e) {
        e.printStackTrace();
      }//end catch
    }//end while loop
  }//end run method
  //-------------------------------------------//

  private Sprite makeSprite(
                Point position, int imageIndex) {
    return new Sprite(
                    this,
                    gifImages[imageIndex],
                    position,
                    new Point(rand.nextInt() % 5,
                            rand.nextInt() % 5));
  }//end makeSprite()
  //-------------------------------------------//

  //Overridden graphics update method on the
  // Frame
  public void update(Graphics g) {
    //Create the offscreen graphics context
    if (offScreenGraphicsCtx == null) {
      offScreenImage = createImage(
                               getSize().width,
                               getSize().height);
      offScreenGraphicsCtx =
                    offScreenImage.getGraphics();
    }//end if

    // Draw the sprites offscreen
    spriteManager.drawScene(
                           offScreenGraphicsCtx);

    // Draw the scene onto the screen
    if(offScreenImage != null){g.drawImage(
                     offScreenImage, 0, 0, this);
    }//end if
  }//end overridden update method
  //-------------------------------------------//

  //Overridden paint method on the Frame
  public void paint(Graphics g) {
    //Nothing required here.  All
    // drawing is done in the update
    // method above.
  }//end overridden paint method

}//end class Inew2338_132
//=============================================//

class BackgroundImage{
  private Image image;
  private Component component;
  private Dimension size;

  public BackgroundImage(Component component,
                                   Image image) {
    this.component = component;
    size = component.getSize();
    this.image = image;
  }//end construtor

  public Dimension getSize(){
    return size;
  }//end getSize()

  public Image getImage(){
    return image;
  }//end getImage()

  public void setImage(Image image){
    this.image = image;
  }//end setImage()

  public void drawBackgroundImage(Graphics g) {
    g.drawImage(image, 0, 0, component);
  }//end drawBackgroundImage()
}//end class BackgroundImage
//=============================================//

class SpriteManager extends Vector {
  private BackgroundImage backgroundImage;

  public SpriteManager(//constructor
               BackgroundImage backgroundImage) {
    this.backgroundImage = backgroundImage;
  }//end constructor
  //-------------------------------------------//

  public Point getEmptyPosition(
                           Dimension spriteSize){
    Rectangle trialSpaceOccupied =
                            new Rectangle(0, 0,
                              spriteSize.width,
                              spriteSize.height);
    Random rand = new Random(
                     System.currentTimeMillis());
    boolean empty = false;
    int numTries = 0;

    // Search for an empty position
    while (!empty && numTries++ < 100){
      // Get a trial position
      trialSpaceOccupied.x =
                        Math.abs(rand.nextInt() %
                                backgroundImage.
                                getSize().width);
      trialSpaceOccupied.y =
                        Math.abs(rand.nextInt() %
                               backgroundImage.
                               getSize().height);

      // Iterate through existing sprites,
      // checking if position is empty
      boolean collision = false;
      for(int cnt = 0;cnt < size();cnt++){
        Rectangle testSpaceOccupied =
                        ((Sprite)elementAt(cnt)).
                              getSpaceOccupied();
        if (trialSpaceOccupied.intersects(
                             testSpaceOccupied)){
          collision = true;
        }//end if
      }//end for loop
      empty = !collision;
    }//end while loop
    return new Point(trialSpaceOccupied.x,
                           trialSpaceOccupied.y);
  }//end getEmptyPosition()
  //-------------------------------------------//

  public void update() {
    Sprite sprite;

    //Iterate through sprite list
    for (int cnt = 0;cnt < size();cnt++){
      sprite = (Sprite)elementAt(cnt);
      //Update a sprite's position
      sprite.updatePosition();

      //Test for collision. Positive
      // result indicates a collision
      int hitIndex = testForCollision(sprite);
      if (hitIndex >= 0){
        //A collision has occurred
        bounceOffSprite(cnt,hitIndex);
      }//end if
    }//end for loop
  }//end update
  //------------------------------------------//

  private int testForCollision(
                             Sprite testSprite) {
    //Check for collision with other
    // sprites
    Sprite  sprite;
    for (int cnt = 0;cnt < size();cnt++){
      sprite = (Sprite)elementAt(cnt);
      if (sprite == testSprite)
        //Don't check self
        continue;
      //Invoke testCollision method of Sprite
      // class to perform the actual test.
      if (testSprite.testCollision(sprite))
        //Return index of colliding sprite
        return cnt;
    }//end for loop
    return -1;//No collision detected
  }//end testForCollision()
  //-------------------------------------------//

  private void bounceOffSprite(
                              int oneHitIndex,
                              int otherHitIndex){
    //Swap motion vectors for bounce algorithm
    Sprite oneSprite =
                  (Sprite)elementAt(oneHitIndex);
    Sprite otherSprite =
                (Sprite)elementAt(otherHitIndex);
    Point swap = oneSprite.getMotionVector();
    oneSprite.setMotionVector(
                  otherSprite.getMotionVector());
    otherSprite.setMotionVector(swap);
  }//end bounceOffSprite()
  //-------------------------------------------//

  public void drawScene(Graphics g){
    //Draw the background and erase sprites from
    // graphics area
    //Disable the following statement for an
    // interesting effect.
    backgroundImage.drawBackgroundImage(g);

    //Iterate through sprites, drawing each
    // sprite
    for (int cnt = 0;cnt < size();cnt++)
      ((Sprite)elementAt(cnt)).
                              drawSpriteImage(g);
  }//end drawScene()
  //-------------------------------------------//

  public void addSprite(Sprite sprite){
    add(sprite);
  }//end addSprite()

}//end class SpriteManager
//=============================================//

class Sprite {
  private Component component;
  private Image image;
  private Rectangle spaceOccupied;
  private Point motionVector;
  private Rectangle bounds;
  private Random rand;

  public Sprite(Component component,
                Image image,
                Point position,
                Point motionVector){
    //Seed a random number generator for this
    // sprite with the sprite position.
    rand = new Random(position.x);
    this.component = component;
    this.image = image;
    setSpaceOccupied(new Rectangle(
                    position.x,
                    position.y,
                    image.getWidth(component),
                    image.getHeight(component)));
    this.motionVector = motionVector;
    //Compute edges of usable graphics area in
    // the Frame.
    int topBanner = ((Container)component).
                                 getInsets().top;
    int bottomBorder =((Container)component).
                              getInsets().bottom;
    int leftBorder = ((Container)component).
                                getInsets().left;
    int rightBorder = ((Container)component).
                               getInsets().right;
    bounds = new Rectangle(
                  0 + leftBorder,
                  0 + topBanner,
                  component.getSize().width -
                    (leftBorder + rightBorder),
                  component.getSize().height -
                    (topBanner + bottomBorder));
  }//end constructor
  //-------------------------------------------//

  public Rectangle getSpaceOccupied(){
    return spaceOccupied;
  }//end getSpaceOccupied()
  //-------------------------------------------//

  void setSpaceOccupied(Rectangle spaceOccupied){
    this.spaceOccupied = spaceOccupied;
  }//setSpaceOccupied()
  //-------------------------------------------//

  public void setSpaceOccupied(Point position){
    spaceOccupied.setLocation(
                         position.x, position.y);
  }//setSpaceOccupied()
  //-------------------------------------------//

  public Point getMotionVector(){
    return motionVector;
  }//end getMotionVector()
  //-------------------------------------------//

  public void setMotionVector(
                             Point motionVector){
    this.motionVector = motionVector;
  }//end setMotionVector()
  //-------------------------------------------//

  public void setBounds(Rectangle bounds){
    this.bounds = bounds;
  }//end setBounds()
  //-------------------------------------------//

  public void updatePosition() {
    Point position = new Point(
               spaceOccupied.x, spaceOccupied.y);

    //Insert random behavior.  During each
    // update, a sprite has about one chance in
    // 10 of making a random change to its
    // motionVector.  When a change occurs, the
    // motionVector coordinate values are forced
    // to fall between -7 and 7.  This puts a
    // cap on the maximum speed for a sprite.
    if(rand.nextInt() % 10 == 0){
      Point randomOffset =
                    new Point(rand.nextInt() % 3,
                             rand.nextInt() % 3);
      motionVector.x += randomOffset.x;
      if(motionVector.x >= 7)motionVector.x -= 7;
      if(motionVector.x <= -7)
                             motionVector.x += 7;
      motionVector.y += randomOffset.y;
      if(motionVector.y >= 7)
                             motionVector.y -= 7;
      if(motionVector.y <= -7)
                             motionVector.y += 7;
    }//end if

    //Move the sprite on the screen
    position.translate(
                 motionVector.x, motionVector.y);

    //Bounce off the walls
    boolean bounceRequired = false;
    Point tempMotionVector = new Point(
                                 motionVector.x,
                                 motionVector.y);

    //Handle walls in x-dimension
    if (position.x < bounds.x) {
      bounceRequired = true;
      position.x = bounds.x;
      //reverse direction in x
      tempMotionVector.x = -tempMotionVector.x;
    }else if ((position.x + spaceOccupied.width)
                    > (bounds.x + bounds.width)){
      bounceRequired = true;
      position.x = bounds.x + bounds.width -
                             spaceOccupied.width;
      //reverse direction in x
      tempMotionVector.x = -tempMotionVector.x;
    }//end else if

    //Handle walls in y-dimension
    if (position.y < bounds.y){
      bounceRequired = true;
      position.y = bounds.y;
      tempMotionVector.y = -tempMotionVector.y;
    }else if ((position.y + spaceOccupied.height)
                   > (bounds.y + bounds.height)){
      bounceRequired = true;
      position.y = bounds.y + bounds.height -
                            spaceOccupied.height;
      tempMotionVector.y = -tempMotionVector.y;
    }//end else if

    if(bounceRequired)//save new motionVector
               setMotionVector(tempMotionVector);
    //update spaceOccupied
    setSpaceOccupied(position);
  }//end updatePosition()
  //-------------------------------------------//

  public void drawSpriteImage(Graphics g){
    g.drawImage(image,
                spaceOccupied.x,
                spaceOccupied.y,
                component);
  }//end drawSpriteImage()
  //-------------------------------------------//

  public boolean testCollision(
                              Sprite testSprite){
    //Check for collision with another sprite
    if (testSprite != this){
      return spaceOccupied.intersects(
                  testSprite.getSpaceOccupied());
    }//end if
    return false;
  }//end testCollision
}//end Sprite class
//=============================================//


Background image gif file

Six image gif files used for animation

True or false?  The above program uses the gif images shown above to produce the output shown in the following screen shot.  When viewed at runtime, the worms constructed from the balls continue to increase in length.

Program Output

Answer and Explanation

3.  The following box contains a Java program.  Assume that the directory containing the compiled version of the Java program also contains an audio file of type au named Inew2338_134.au.  The format of the audio file can be described as follows:

PCM_SIGNED, 11025.0 Hz, 8 bit, mono, audio data

The computer on which this program is run must contain a compatible sound card and a pair of speakers.

True or false?  The audio file is played through the speakers when the program starts running.  The program continues looping and re-playing the audio file until the user manually terminates the program.

import java.io.*;
import javax.sound.sampled.*;

public class Inew2338_134{

  AudioFormat audioFormat;
  AudioInputStream audioInputStream;
  SourceDataLine sourceDataLine;

  final String fileName = "Inew2338_134.au";
  static PlayThread playThread;

  public static void main(String args[]){
    try{
      Inew2338_134 theObj = new Inew2338_134();
      theObj.playAudio();

      while(true){
        Thread.currentThread().sleep(500);
        if(!playThread.isAlive()){
          theObj.playAudio();
        }//end if
      }//end while loop
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end main
  //-------------------------------------------//

  //This method plays back audio data from an
  // audio file whose name is specified in
  // fileName.
  private void playAudio() {
    try{
      File soundFile = new File(fileName);
      audioInputStream = AudioSystem.
                  getAudioInputStream(soundFile);
      audioFormat = audioInputStream.getFormat();
      System.out.println(audioFormat);

      DataLine.Info dataLineInfo =
                          new DataLine.Info(
                            SourceDataLine.class,
                                    audioFormat);

      sourceDataLine =
             (SourceDataLine)AudioSystem.getLine(
                                   dataLineInfo);

      //Create a thread to play back the data and
      // start it running.
      playThread = new PlayThread();
      playThread.start();
    }catch (Exception e) {
      e.printStackTrace();
      System.exit(0);
    }//end catch
  }//end playAudio

//=============================================//
//Inner class to play back the data from the
// audio file.
class PlayThread extends Thread{
  byte tempBuffer[] = new byte[10000];

  public void run(){
    try{
      sourceDataLine.open(audioFormat);
      sourceDataLine.start();

      int cnt;
      //Keep looping until the input read method
      // returns -1 for empty stream
      while((cnt = audioInputStream.read(
         tempBuffer,0,tempBuffer.length)) != -1){
        if(cnt > 0){
          //Write data to the internal buffer of
          // the data line where it will be
          // delivered to the speaker.
          sourceDataLine.write(
                             tempBuffer, 0, cnt);
        }//end if
      }//end while
      //Block and wait for internal buffer of the
      // data line to empty.
      sourceDataLine.drain();
      sourceDataLine.close();

    }catch (Exception e) {
      e.printStackTrace();
      System.exit(0);
    }//end catch
  }//end run
}//end inner class PlayThread
//===================================//

}//end outer class Inew2338_134.java

Answer and Explanation


Copyright 2004, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which have gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com


Answers and Explanations


Answer 3

True.

Explanation 3

This program demonstrates playback of an audio file of  type au.  The name of the audio file, which must be in the current directory, is hard coded into the program as Inew2338_134.au.

The program plays the audio file on startup and continues looping and re-playing the audio file until the program is manually terminated.

The program displays the format of the audio data in the file before playing the file.  The format is displayed on the command- line screen.

You can learn more about this and other Java sound topics in lessons 2004 through 2026 at http://www.dickbaldwin.com/tocadv.htm.

You might also want go to Google and search for the following keywords:

This might help you to locate some of Prof. Baldwin's publications on these topics that were not included in the lessons listed earlier.  Go to the last page of the Google results and click on the link that reads repeat the search with the omitted results included to make certain that Google didn't omit any links that might be useful to you.

Back to Question 3


Answer 2

False.

Explanation 2

This program produces the output shown in the following screen shot.  When viewed at runtime, the balls move but they don't turn into worms.

Program Output

The screen shot that is shown with the question (that looks like colored worms) was produced by disabling the boldface statement shown in the following method.

  public void drawScene(Graphics g){
    //Draw the background and erase sprites from
    // graphics area
    //Disable the following statement for an
    // interesting effect.
//    backgroundImage.drawBackgroundImage(g);

    //Iterate through sprites, drawing each
    // sprite
    for (int cnt = 0;cnt < size();cnt++)
      ((Sprite)elementAt(cnt)).
                              drawSpriteImage(g);
  }//end drawScene()

When that statement is not disabled (as is the case in the question), this animation program displays several colored balls moving around in an aquarium. Each ball maintains generally the same course until it collides with another ball or with a wall.  It changes course when a collision occurs.  In addition, each ball has the ability to occasionally make random changes in its course.  This change in course is illustrated by the worms, which trace the paths of the balls during animation.  (In the worm version, the ball is drawn in each new position without erasing the drawing of the previous position.)

You can learn more about this topic in lesson 1450 and the lessons following that lesson at http://www.dickbaldwin.com/tocadv.htm.

You might also want go to Google and search for the following keywords:

This might help you to locate some of Prof. Baldwin's publications on these topics that were not included in the lessons listed earlier.  Go to the last page of the Google results and click on the link that reads repeat the search with the omitted results included to make certain that Google didn't omit any links that might be useful to you.

Back to Question 2


Answer 1

False.

Explanation 1

This program illustrates image animation. 

The stick man only dances while the user is pointing to the JFrame with the mouse pointer.  A mouseEntered event causes the animation to begin.  A mouseExited event causes animation to stop.  When the animation stops, the stick man is restored to the original position shown in the screen shot.

The program also illustrates:

You can learn more about this topic in lesson 1470 at http://www.dickbaldwin.com/tocadv.htm.

You might also want go to Google and search for the following keywords:

This might help you to locate some of Prof. Baldwin's publications on these topics that were not included in the lessons listed earlier.  Go to the last page of the Google results and click on the link that reads repeat the search with the omitted results included to make certain that Google didn't omit any links that might be useful to you.

Back to Question 1


Copyright 2004, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which have gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-