Java tutorial
/* * Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002. * All rights reserved. Software written by Ian F. Darwin and others. * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Java, the Duke mascot, and all variants of Sun's Java "steaming coffee * cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's, * pioneering role in inventing and promulgating (and standardizing) the Java * language and environment is gratefully acknowledged. * * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for * inventing predecessor languages C and C++ is also gratefully acknowledged. */ import javax.comm.*; import java.util.*; import java.io.*; import java.lang.*; import java.awt.*; import java.awt.event.*; /** Main program, driver for Plotter class. * This is to simulate a larger graphics application such as GnuPlot. */ public class PlotDriver { /** Construct a Plotter driver, and try it out. */ public static void main(String[] argv) { Plotter r; // if (argv.length != 1) { // System.err.println("Usage: PlotDriver driverclass"); // return; // } try { Class c = Class.forName("PlotterAWT"); Object o = c.newInstance(); if (!(o instanceof Plotter)) throw new ClassNotFoundException("Not instanceof Plotter"); r = (Plotter) o; } catch (ClassNotFoundException e) { System.err.println("Sorry, " + argv[0] + " not a plotter class"); return; } catch (Exception e) { e.printStackTrace(); return; } r.penDown(); r.penColor(1); r.moveTo(200, 200); r.penColor(2); r.drawBox(123, 200); r.rmoveTo(10, 20); r.penColor(3); r.drawBox(123, 200); r.penUp(); r.moveTo(300, 100); r.penDown(); r.setFont("Helvetica", 14); r.drawString("Hello World"); r.penColor(4); r.drawBox(10, 10); } } /** * Plotter abstract class. Must be subclassed * for X, DOS, Penman, HP plotter, etc. * * Coordinate space: X = 0 at left, increases to right. * Y = 0 at top, increases downward (same as AWT). * * @author Ian F. Darwin */ abstract class Plotter { public final int MAXX = 800; public final int MAXY = 600; /** Current X co-ordinate (same reference frame as AWT!) */ protected int curx; /** Current Y co-ordinate (same reference frame as AWT!) */ protected int cury; /** The current state: up or down */ protected boolean penIsUp; /** The current color */ protected int penColor; Plotter() { penIsUp = true; curx = 0; cury = 0; } abstract void rmoveTo(int incrx, int incry); abstract void moveTo(int absx, int absy); abstract void penUp(); abstract void penDown(); abstract void penColor(int c); abstract void setFont(String fName, int fSize); abstract void drawString(String s); /* Concrete methods */ /** Draw a box of width w and height h */ public void drawBox(int w, int h) { penDown(); rmoveTo(w, 0); rmoveTo(0, h); rmoveTo(-w, 0); rmoveTo(0, -h); penUp(); } /** Draw a box given an AWT Dimension for its size */ public void drawBox(java.awt.Dimension d) { drawBox(d.width, d.height); } /** Draw a box given an AWT Rectangle for its location and size */ public void drawBox(java.awt.Rectangle r) { moveTo(r.x, r.y); drawBox(r.width, r.height); } } /** * A Plotter subclass for drawing into an AWT Window. Reflecting back * to AWT gives us a "known working" plotter to test on. * You can also steal this as a basis for your own plotter driver. * @author Ian Darwin */ class PlotterAWT extends Plotter { Frame f; PCanvas p; Graphics g; Font font; FontMetrics fontMetrics; PlotterAWT() { super(); f = new Frame("Plotter"); p = new PCanvas(MAXX, MAXY); f.add(p); f.pack(); f.setVisible(true); g = p.getOsGraphics(); } public void drawBox(int w, int h) { g.drawRect(curx, cury, w, h); p.repaint(); } public void rmoveTo(int incrx, int incry) { moveTo(curx += incrx, cury += incry); } public void moveTo(int absx, int absy) { if (!penIsUp) g.drawLine(curx, cury, absx, absy); curx = absx; cury = absy; } public void setdir(float deg) { } void penUp() { penIsUp = true; } void penDown() { penIsUp = false; } void penColor(int c) { switch (c) { case 0: g.setColor(Color.white); break; case 1: g.setColor(Color.black); break; case 2: g.setColor(Color.red); break; case 3: g.setColor(Color.green); break; case 4: g.setColor(Color.blue); break; default: g.setColor(new Color(c)); break; } } void setFont(String fName, int fSize) { font = new Font(fName, Font.BOLD, fSize); fontMetrics = p.getFontMetrics(font); } void drawString(String s) { g.drawString(s, curx, cury); curx += fontMetrics.stringWidth(s); } /** A Member Class that contains an off-screen Image that is * drawn into; this component's paint() copies from there to * the screen. This avoids having to keep a list of all the * things that have been drawn. */ class PCanvas extends Canvas { Image offScreenImage; int width; int height; Graphics pg; PCanvas(int w, int h) { width = w; height = h; setBackground(Color.white); setForeground(Color.red); } public Graphics getOsGraphics() { return pg; } /** This is called by AWT after the native window peer is created, * and before paint() is called for the first time, so * is a good time to create images and the like. */ public void addNotify() { super.addNotify(); offScreenImage = createImage(width, height); // assert (offScreenImage != null); pg = offScreenImage.getGraphics(); } public void paint(Graphics pg) { pg.drawImage(offScreenImage, 0, 0, null); } public Dimension getPreferredSize() { return new Dimension(width, height); } } } /** Plotter class for testing higher-level software. */ class PlotterDummy extends Plotter { /** Constructor: nothing to do */ PlotterDummy() { super(); } /** move to absolute location */ void moveTo(int absx, int absy) { curx = absx; cury = absy; System.out.println("moveTo [" + curx + "," + cury + "]"); } /** move to relative location */ void rmoveTo(int incrx, int incry) { curx += incrx; cury += incry; System.out.println("rmoveTo [" + curx + "," + cury + "]"); } public void setFont(java.lang.String fName, int fSize) { System.out.println("set Font to " + fName); } public void drawString(java.lang.String s) { System.out.println("Draw the string \"" + s + "\""); } void setPenState(boolean up) { penIsUp = up; System.out.println("Pen Up is [" + penIsUp + "]"); } void penUp() { setPenState(true); } void penDown() { setPenState(false); } void penColor(int c) { penColor = c; System.out.println("PenColor is [" + penColor + "]"); } } /** * A Plotter subclass for drawing on a Penman plotter. * These were made in the UK and sold into North American markets. * It is a little "turtle" style robot plotter that communicates * over a serial port. For this, we use the "Java Communicatons" API. * Java Communications is a "standard extention" and must be downloaded * and installed separately from the JDK before you can even compile this * program. * * @author Ian Darwin, http://www.darwinsys.com/ */ class Penman extends Plotter { private final String OK_PROMPT = "\r\n!"; private final int MAX_REPLY_BYTES = 50; // paranoid upper bound private SerialPort tty; private DataInputStream is; private DataOutputStream os; /** Construct a Penman plotter object */ public Penman() throws NoSuchPortException, PortInUseException, IOException, UnsupportedCommOperationException { super(); init_comm("COM2"); // setup serial commx init_plotter(); // set plotter to good state } private void init_plotter() { send("I"); expect('!'); // eat VERSION etc., up to ! send("I"); expect('!'); // wait for it! send("H"); // find home position expect('!'); // wait for it! send("A"); // Set to use absolute coordinates expect('!'); curx = cury = 0; penUp(); } // // PUBLIC DRAWING ROUTINES // public void setFont(String fName, int fSize) { // Font name is ignored for now... // Penman's size is in mm, fsize in points (inch/72). int size = (int) (fSize * 25.4f / 72); send("S" + size + ","); expect(OK_PROMPT); System.err.println("Font set request: " + fName + "/" + fSize); } public void drawString(String mesg) { send("L" + mesg + "\r"); expect(OK_PROMPT); } /** Move to a relative location */ public void rmoveTo(int incrx, int incry) { moveTo(curx + incrx, cury + incry); } /** move to absolute location */ public void moveTo(int absx, int absy) { System.err.println("moveTo [" + absx + "," + absy + "]"); curx = absx; cury = absy; send("M" + curx + "," + cury + ","); expect(OK_PROMPT); } private void setPenState(boolean up) { penIsUp = up; System.err.println("Pen Up is [" + penIsUp + "]"); } public void penUp() { setPenState(true); send("U"); expect(OK_PROMPT); } public void penDown() { setPenState(false); send("D"); expect(OK_PROMPT); } public void penColor(int c) { penColor = (c % 3) + 1; // only has 3 pens, 4->1 System.err.println("PenColor is [" + penColor + "]"); send("P" + c + ","); expect(OK_PROMPT); } // // PRIVATE COMMUNICATION ROUTINES // /** Set up communication. * <br/> * XXX: Should probably re-use CommPortOpen instead. */ private void init_comm(String portName) throws NoSuchPortException, PortInUseException, IOException, UnsupportedCommOperationException { // get list of ports available on this particular computer. // Enumeration pList = CommPortIdentifier.getPortIdentifiers(); // Print the list. A GUI program would put these in a chooser! // while (pList.hasMoreElements()) { // CommPortIdentifier cpi = (CommPortIdentifier)pList.nextElement(); // System.err.println("Port " + cpi.getName()); // } // Open a port. CommPortIdentifier port = CommPortIdentifier.getPortIdentifier(portName); // This form of open takes an Application Name and a timeout. tty = (SerialPort) port.open("Penman Driver", 1000); // set up the serial port tty.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); tty.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_OUT | SerialPort.FLOWCONTROL_RTSCTS_OUT); // Get the input and output streams is = new DataInputStream(tty.getInputStream()); os = new DataOutputStream(tty.getOutputStream()); } /** Send a command to the plotter. Although the argument is a String, * we send each char as a *byte*, so avoid 16-bit characters! * Not that it matters: the Penman only knows about 8-bit chars. */ private void send(String s) { System.err.println("sending " + s + "..."); try { for (int i = 0; i < s.length(); i++) os.writeByte(s.charAt(i)); } catch (IOException e) { e.printStackTrace(); } } /** Expect a given CHAR for a result */ private void expect(char s) { byte b; try { for (int i = 0; i < MAX_REPLY_BYTES; i++) { if ((b = is.readByte()) == s) { return; } System.err.print((char) b); } } catch (IOException e) { System.err.println("Penman:expect(char " + s + "): Read failed"); System.exit(1); } System.err.println("ARGHH!"); } /** Expect a given String for a result */ private void expect(String s) { byte ans[] = new byte[s.length()]; System.err.println("expect " + s + " ..."); try { is.read(ans); } catch (IOException e) { System.err.println("Penman:expect(String " + s + "): Read failed"); System.exit(1); } ; for (int i = 0; i < s.length() && i < ans.length; i++) if (ans[i] != s.charAt(i)) { System.err.println("MISMATCH"); break; } System.err.println("GOT: " + new String(ans)); } }