This page contains a selection of samples written in Java:
This small program shows how to make simple use of the facilities of the java.awt.datatransfer and java.awt.dnd packages. To run it, just compile the source file below, and run it with no arguments.
--- Drag1.java ---import java.awt.*; import java.awt.event.*; import java.awt.dnd.*; import java.awt.datatransfer.*; import javax.swing.*; import java.io.*; public class Drag1 extends JFrame implements DragGestureListener, DragSourceListener, DropTargetListener { static String items[] = { "Item 1", "Item No. 2", "The Third Item!" }; Container cpane; DropTarget mytarget; DragSource mysource; DragGestureRecognizer dgr; JLabel source1; JList list1; JTextArea text1; DefaultListModel lmod; public Drag1() { super("Drag1 Test"); cpane = getContentPane(); cpane.setLayout(new BorderLayout()); source1 = new JLabel("Drag Source Label - drag me!"); lmod = new DefaultListModel(); list1 = new JList(lmod); for(int i = 0; i < items.length; i++) lmod.addElement(items[i]); text1 = new JTextArea("Drag from the label to\nthe list, then drop!", 5, 24); mysource = new DragSource(); mytarget = new DropTarget(list1, DnDConstants.ACTION_COPY_OR_MOVE, this); dgr = mysource.createDefaultDragGestureRecognizer(source1, DnDConstants.ACTION_COPY_OR_MOVE, this); cpane.add("North", source1); cpane.add("Center", list1); cpane.add("South", text1); text1.setBackground(Color.pink); setBounds(100, 100, 320, 460); setVisible(true); } public void dragGestureRecognized(DragGestureEvent e) { String sel = source1.getText(); e.startDrag(DragSource.DefaultCopyDrop, new StringSelection(sel), this); } public void dragEnter(DropTargetDragEvent e) { System.out.println("dragEnter"); e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); } public void drop(DropTargetDropEvent e) { System.out.println("drop"); try { if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) { Transferable tr = e.getTransferable(); e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); String s = (String)tr.getTransferData(DataFlavor.stringFlavor); lmod.addElement(s); e.dropComplete(true); } else { e.rejectDrop(); } } catch (Exception ex) { System.out.println("Data transfer exception: " + ex); } } public void dragExit(DropTargetEvent e) { System.out.println("dragExit"); } public void dragOver(DropTargetDragEvent e) { System.out.println("dragOver (drop)"); } public void dropActionChanged(DropTargetDragEvent e) { System.out.println("dropActionChanged"); } public void dragDropEnd(DragSourceDropEvent e) { System.out.println("dragDropEnd"); } public void dragEnter(DragSourceDragEvent e) { System.out.println("dragEnter"); } public void dragExit(DragSourceEvent e) { System.out.println("dragExit"); } public void dragOver(DragSourceDragEvent e) { System.out.println("dragOver (drag)"); } public void dropActionChanged(DragSourceDragEvent e) { System.out.println("dragActionChanged (drag)"); } public static void main(String [] args) { Drag1 d1; d1 = new Drag1(); d1.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { System.exit(0); } } ); } }
This small program illustrates simple recursion and use of the java.io.File class facilities. To run it, just compile the source file below, and run it with a command-line argument of the base directory you want to examine. This sample requires JDK 1.2.1 or later, but by commenting out just one line, it can run with JDK 1.1.7. --- FileWalk.java ---
import java.io.*; import java.util.*; public class FileWalk implements Runnable { protected File base; protected int indent; protected PrintStream out; /** * Create a FileWalk object for the given string, with * a print indentation given. */ public FileWalk(String filename, int i) { base = new File(filename); indent = i; out = System.out; } static class Stats { public int totalCount; public long totalSize; public Stats() { totalCount = 0; totalSize = 0; } public void add(File f) { totalCount++; if (!f.isDirectory()) { totalSize += f.length(); } } public void add(Stats s) { totalCount += s.totalCount; totalSize += s.totalSize; } public String toString() { return "Files: " + totalCount + ", total size: " + totalSize; } } public void setOutput(PrintStream ps) { out = ps; } public void run() { Stats stats = new Stats(); process(base, 0, indent, out, stats); if (stats.totalCount > 1) out.println("Grand total: " + stats); } static String dup(char c, int i) { StringBuffer bt = new StringBuffer(i); while(i > 0) { bt.append(c); i--; } return bt.toString(); } static void process(File b, int ind, int incr, PrintStream ps, Stats s) { try { if (b.isDirectory()) { s.add(b); ps.print(dup(' ',ind)); ps.print("Directory " + b.getCanonicalPath()); ps.print(" ["); ps.print((b.canRead())?("readable,"):("not readable,")); ps.print((b.canWrite())?("writable"):("not writable")); ps.print("]\n"); Stats mys = new Stats(); String [] subs = b.list(); if (subs.length > 0) { // if you don't have Java 2 platform, comment out this line Arrays.sort(subs); // for(int i = 0; i < subs.length; i++) { File f2 = new File(subs[i]); if (f2.equals(b)) continue; if (f2.equals(b.getParentFile())) continue; // recurse here! process(new File(b,subs[i]), ind + incr, incr, ps, mys); } ps.print(dup(' ',ind)); ps.println(b.getCanonicalPath() + " " + mys); } else { ps.print(dup(' ',ind)); ps.println(b.getCanonicalPath() + " - empty directory"); } } else { s.add(b); ps.print(dup(' ',ind)); ps.print(b.getName()); ps.print(" size: " + b.length() + " bytes ["); ps.print((b.canRead())?("readable,"):("not readable,")); ps.print((b.canWrite())?("writable"):("not writable")); ps.print("]\n"); } } catch (IOException ie) { ps.println("Exception on path: " + ie); } } public static void main(String [] args) { String basename = "."; if (args.length > 0) { basename = args[0]; } System.out.println("Base name is: " + basename); FileWalk walker = new FileWalk(basename, 4); walker.setOutput(System.out); walker.run(); } }
The Java Platform 2 includes HTML support as part of the Java Foundation Classes (Swing). This small program shows how to use the HTMLEditorKit.Parser and ParserDelegator classes to parse HTML code from a file or URL. This sample requires JDK 1.2.1 or later.
--- ---import java.io.*; import java.net.*; import javax.swing.text.*; import javax.swing.text.html.*; import javax.swing.text.html.parser.*; /** * This small demo program shows how to use the * HTMLEditorKit.Parser and its implementing class * ParserDelegator in the Swing system. */ public class HtmlParseDemo { public static void main(String [] args) { Reader r; if (args.length == 0) { System.err.println("Usage: java HTMLParseDemo [url | file]"); System.exit(0); } String spec = args[0]; try { if (spec.indexOf("://") > 0) { URL u = new URL(spec); Object content = u.getContent(); if (content instanceof InputStream) { r = new InputStreamReader((InputStream)content); } else if (content instanceof Reader) { r = (Reader)content; } else { throw new Exception("Bad URL content type."); } } else { r = new FileReader(spec); } HTMLEditorKit.Parser parser; System.out.println("About to parse " + spec); parser = new ParserDelegator(); parser.parse(r, new HTMLParseLister(), true); r.close(); } catch (Exception e) { System.err.println("Error: " + e); e.printStackTrace(System.err); } } } /** * HTML parsing proceeds by calling a callback for * each and every piece of the HTML document. This * simple callback class simply prints an indented * structural listing of the HTML data. */ class HTMLParseLister extends HTMLEditorKit.ParserCallback { int indentSize = 0; protected void indent() { indentSize += 3; } protected void unIndent() { indentSize -= 3; if (indentSize < 0) indentSize = 0; } protected void pIndent() { for(int i = 0; i < indentSize; i++) System.out.print(" "); } public void handleText(char[] data, int pos) { pIndent(); System.out.println("Text(" + data.length + " chars)"); } public void handleComment(char[] data, int pos) { pIndent(); System.out.println("Comment(" + data.length + " chars)"); } public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { pIndent(); System.out.println("Tag start(<" + t.toString() + ">, " + a.getAttributeCount() + " attrs)"); indent(); } public void handleEndTag(HTML.Tag t, int pos) { unIndent(); pIndent(); System.out.println("Tag end(" + t.toString() + ">)"); } public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) { pIndent(); System.out.println("Tag(<" + t.toString() + ">, " + a.getAttributeCount() + " attrs)"); } public void handleError(String errorMsg, int pos){ System.out.println("Parsing error: " + errorMsg + " at " + pos); } }
Several XML parsers have been written for Java. One set is called Project X and it was released by JavaSoft. A stable and mature XML implementation, the Project X packages are easy to use and pretty fast. There are two XML parsing models supported by the packages: the serial access model (SAX) and the document object model (DOM). Both programs parse an XML file, and display the results in an indented list. XmlView1 shows off SAX, and XmlView2 shows off DOM. Both of them feature very simple AWT interfaces.
--- XmlView1.java ---import java.io.*; import java.awt.*; import java.awt.event.*; import org.xml.sax.*; import com.sun.xml.parser.*; import org.xml.sax.helpers.*; /** * XmlView1 - a small AWT GUI program that illustrates use * of the javasoft SAX implementation. Note that this * little program cannot parse any XML file that specifies * a DTD, because SAX is very fussy about DTD URIs. * XmlView2 is more sophisticated, and can handle XML files * that have DTDs. */ public class XmlView1 extends Frame implements ActionListener { List contents; Label caption; TextField filename; Button goButton; /** * Constructor - create the AWT GUI */ public XmlView1() { super("XmlView1"); contents = new List(); caption = new Label("XML file:"); filename = new TextField(); goButton = new Button("PARSE"); Panel topPanel = new Panel(); topPanel.setLayout(new BorderLayout(2,2)); topPanel.add("West",caption); topPanel.add("Center",filename); topPanel.add("East", goButton); add("North", topPanel); add("Center", contents); setSize(400,500); goButton.addActionListener(this); } /** * This class is a SAX listener that accepts the parse * events during parsing. */ public class ListingHandler extends HandlerBase { int depth = 0; int count = 0; private String indent(String t) { StringBuffer s = new StringBuffer(depth + t.length()); for(int i = 0; i < depth; i++) s.append(" "); s.append(t); return s.toString(); } public void characters(char [] ch, int start, int length) { String s = indent("CHARS" + length + ": "); s += new String(ch, start, length); contents.add(s); } public void endDocument() { depth -= 1; String s = indent("EndDocument"); contents.add(s); } public void endElement(String name) { depth -= 1; String s = indent("EndElement: "); s += name; contents.add(s); } public void warning(SAXParseException e) { depth -= 1; String s = indent("Warning! "); s += e.toString(); contents.add(s); } public void error(SAXParseException e) { depth -= 1; String s = indent("ERROR! "); s += e.toString(); contents.add(s); } public void fatalError(SAXParseException e) { depth = 0; String s = indent("FATAL ERROR!!! "); s += e.toString(); contents.add(s); } public void processingInstruction(String target, String data) { String s = indent("ProcInstr: "); s += target; s += ""; s += data; contents.add(s); } public void startDocument() { depth = 0; String s = "StartDocument"; contents.add(s); depth += 1; count += 1; } public void startElement(String name, AttributeList alist) { String s = indent("StartElement: "); s += name; contents.add(s); depth += 3; doAttributeList(alist); depth -= 3; depth += 1; count += 1; } private void doAttributeList(AttributeList a) { int i; String s; for(i = 0; i < a.getLength(); i++) { s = indent("Attribute: "); s += a.getName(i); s += "="; s += a.getValue(i); contents.add(s); } } } /** * Start the parsing when the user tells us to do so. */ public void actionPerformed(ActionEvent e) { String fn = filename.getText(); FileInputStream fis; try { fis = new FileInputStream(fn); } catch (IOException ex2) { System.out.println("Bad file: " + fn); return; } System.out.println("Creating InputSource..."); InputSource is = new InputSource(fis); System.out.println("Creating and configuring Parser..."); com.sun.xml.parser.Parser myparser = new com.sun.xml.parser.Parser(); myparser.setFastStandalone(true); org.xml.sax.Parser p = myparser; ListingHandler lh = new ListingHandler(); p.setDocumentHandler(lh); p.setErrorHandler(lh); System.out.println("Clearing list.."); contents.removeAll(); try { System.out.println("Parsing.."); p.parse(is); System.out.println("Done, " + lh.count + " elements parsed."); } catch (Exception se) { System.out.println("Parsing exception: " + se); contents.add("Parsing Exception Thrown!!!"); } try { fis.close(); } catch (IOException e4) { } } /** * Main method - kick off the program. */ public static void main(String args[]) { XmlView1 v1 = new XmlView1(); v1.show(); v1.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.out.println("All done. Bye."); System.exit(0); } } ); } }--- XmlView2.java ---
import java.io.*; import java.awt.*; import java.net.*; import java.awt.event.*; import org.xml.sax.*; import org.w3c.dom.*; import com.sun.xml.parser.*; import com.sun.xml.tree.*; import org.xml.sax.helpers.*; /* * XmlView2 - a small AWT GUI program that illustrates use * of the Javasoft "Project X" XML document interface. * To use this program, simply run it, then enter the URL * of an XML file in the text field at the top. */ public class XmlView2 extends Frame implements ActionListener { List contents; Label caption; TextField filename; Button goButton; TextArea status; // Names of types from org.w3c.dom.Node. public static final String [] typeNames = { "unknown", "element", "attr", "text", "CDATASection", "EntityReference", "Entity", "ProcessingInstruction", "Comment", "Document", "DocumentType", "DocumentFragment", "Notation", }; /** * Constructor - create the AWT interface. */ public XmlView2() { super("XmlView2 - DOM"); contents = new List(); caption = new Label("XML Source URL:", Label.RIGHT); filename = new TextField(); goButton = new Button("PARSE"); Panel npanel = new Panel(); npanel.setLayout(new BorderLayout(2,2)); Panel topPanel = new Panel(); topPanel.setLayout(new BorderLayout(2,0)); topPanel.add("West",caption); topPanel.add("Center",filename); topPanel.add("East", goButton); npanel.add("North", topPanel); add("Center", contents); status = new TextArea(4,40); npanel.add("South", status); contents.setBackground(Color.white); add("North", npanel); setSize(490,620); setLocation(140,60); goButton.addActionListener(this); filename.addActionListener(this); } /** * Log a message to the scrolling text area. */ private void logMessage(String s) { status.append(s); status.append("\n"); try { Thread.sleep(40); } catch (InterruptedException ie) { } } /** * Prepend indentation onto the front of a string, return * result as a StringBuffer. */ private StringBuffer indent(int depth, String t) { StringBuffer s = new StringBuffer(depth + t.length()); for(int i = 0; i < depth; i++) s.append(" "); s.append(t); return s; } /** * Recursively dump an XmlDocument or other Node into * the AWT List contents. * The parameter depth is the indentation amount, not * strictly the tree depth. */ public void dumpXmlNode(Node n, int depth) { StringBuffer sb; NamedNodeMap attrs = n.getAttributes(); if (n instanceof XmlDocument) { sb = indent(depth, "Document Start (type="); DocumentType dt = ((XmlDocument)n).getDoctype(); if (dt == null) sb.append("unknown"); else sb.append(dt.getName()); sb.append(")"); contents.add(sb.toString()); } else { sb = indent(depth, "Node: "); sb.append(n.getNodeName()); sb.append(" (type="); int typ = n.getNodeType(); if (typ >= 0 && typ < typeNames.length) sb.append(typeNames[typ] + ")"); else sb.append(typ + ")"); sb.append(" (attrs="); sb.append(((attrs==null)?(0):(attrs.getLength())) + ")"); String val = n.getNodeValue(); if (val != null) { sb.append(" - "); sb.append(val); } contents.add(sb.toString()); } if (attrs != null && attrs.getLength() > 0) { Node nc; int d = depth + 3; for(int i = 0; i < attrs.getLength(); i++) { nc = attrs.item(i); dumpXmlNode(nc, d); } } if (n.hasChildNodes()) { Node nc; int d = depth + 1; for(nc = n.getFirstChild(); nc != null; nc = nc.getNextSibling()) { dumpXmlNode(nc, d); } } } /** * React to the user telling us to do the parsing. */ boolean running = false; public synchronized void actionPerformed(ActionEvent e) { if (running) { logMessage("Already busy, please wait..."); return; } String fn = filename.getText(); ParThread pt = new ParThread(fn); running = true; pt.setPriority(Thread.NORM_PRIORITY - 1); pt.start(); } /** * This thread class does all the actual parsing and * displaying. Good thing AWT List is thread-safe! */ class ParThread extends Thread { String fn; ParThread(String f) { fn = f; } /** * Open the URL, parse the contents into a XmlDocument, * then dump the document into the List. Log lots of * messages to the scrolling text area. */ public void run() { URL u2 = null; long stime = System.currentTimeMillis(); try { URL u1; File f = new File("."); u1 = new URL("file:" + f.getAbsolutePath()); u2 = new URL(u1, fn); } catch (MalformedURLException mue) { logMessage("Bad URL: " + u2); synchronized (XmlView2.this) { running = false; } return; } logMessage("Clearing list.."); contents.removeAll(); XmlDocument doc = null; try { logMessage("Creating document..."); try { doc = XmlDocument.createXmlDocument(u2.toString(), true); logMessage("Parsing with validation succeeded!"); } catch (SAXException sep) { logMessage("Validating parse failed: " + sep); logMessage(" --- trying non-validating."); doc = null; } if (doc == null) { doc = XmlDocument.createXmlDocument(u2.toString(), false); logMessage("Parsing without validation succeeded."); } long etime = System.currentTimeMillis(); logMessage("Parsing done, time = " + (etime - stime) + "ms."); logMessage("Dumping structure to List window..."); dumpXmlNode(doc,0); logMessage("Total nodes in document: " + contents.getItemCount()); } catch (Exception se) { logMessage("Parsing exception: " + se); contents.add("Parsing Exception Thrown!!!"); // se.printStackTrace(); } long ftime = System.currentTimeMillis(); logMessage("Done with " + u2.toString() + "\n total time = " + (ftime - stime) + "ms."); synchronized (XmlView2.this) { running = false; } } } /** * Main routine to kick off the program. Simply creates the * XmlView2 object and shows it. */ public static void main(String args[]) { XmlView2 v2 = new XmlView2(); v2.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.out.println("All done. Bye."); System.exit(0); } } ); v2.show(); } }
This code example illustrates using the Java2D graphics (java.awt.geom) package to draw text along the outside of a circle. This is something that was nearly impossible with straight AWT graphics, but is quite feasible using affine transforms in Java2D. This example requires Java 2 (JDK 1.2.1 or later).
--- CircleTextDemo.java ---import java.awt.*; import java.awt.event.*; import java.awt.geom.*; /** * A demo class that illustrates drawing text * along the outside of a circle. */ public class CircleTextDemo extends Canvas { Frame myframe; TextField text; Button printBtn; Font myfont; Color textcolor; Color circlecolor; /** * Create a CircleTextDemo canvas and frame * with default settings. */ public CircleTextDemo() { this("Serif", Font.PLAIN, 18, Color.pink, Color.black); } /** * Create a CircleTextDemo canvas and frame * with supplied settings. * * @param ff Font family (usually "Serif") * @param fs Font style (usually Font.PLAIN) * @param fz Font size (usually 18) * @param bg Background color for this canvas (usually pink) * @param fg Foreground color for text (usually black) */ public CircleTextDemo(String ff, int fs, int fz, Color bg, Color fg) { setBackground(bg); circlecolor = bg.brighter(); textcolor = fg; myfont = new Font(ff, fs, fz); text = new TextField("Text on a circle using Java 2D Graphics!"); myframe = new Frame("CircleTextDemo"); printBtn = new Button("Print"); myframe.add(text, BorderLayout.NORTH); myframe.add(this, BorderLayout.CENTER); myframe.add(printBtn, BorderLayout.SOUTH); myframe.setSize(new Dimension(300,340)); myframe.setLocation(150,140); myframe.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { System.exit(0); } }); text.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { repaint(); } }); printBtn.addActionListener(new FramePrinter(myframe)); myframe.setVisible(true); } /** * Paint the contents of the CircleDemoText canvas. * * @param g - a Graphics, hopefully a Graphics2D */ public void paint(Graphics g) { String st = text.getText(); if (st.length() == 0) return; if (g instanceof Graphics2D) { Dimension cd = getSize(); Point pt = new Point(cd.width / 2, cd.height / 2); int radius = (int)(pt.x * 0.84); g.setColor(circlecolor); g.drawArc(pt.x - radius, pt.y - radius, radius*2-1, radius*2-1, 0, 360); g.setColor(textcolor); g.setFont(myfont); drawCircleText((Graphics2D)g, st, pt, radius, -Math.PI/2, 1.0); } else { System.out.println("Cannot draw curved text without a Graphics2D"); } } /** * Draw a piece of text on a circular curve, one * character at a time. This is harder than it looks... * * This method accepts many arguments: * g - a Graphics2D ready to be used to draw, * st - the string to draw, * center - the center point of the circle (Point), * r - the radius of the circle, * a1 - the beginning angle on the circle to start, in radians, * af - the angle advance factor (usually 1.0) */ static void drawCircleText(Graphics2D g, String st, Point center, double r, double a1, double af) { double curangle = a1; double curangleSin; Point2D c = new Point2D.Double(center.x, center.y); char ch[] = st.toCharArray(); FontMetrics fm = g.getFontMetrics(); AffineTransform xform1, cxform; xform1 = AffineTransform.getTranslateInstance(c.getX(),c.getY()); for(int i = 0; i < ch.length; i++) { double cwid = (double)(getWidth(ch[i],fm)); if (!(ch[i] == ' ' || Character.isSpaceChar(ch[i]))) { cwid = (double)(fm.charWidth(ch[i])); cxform = new AffineTransform(xform1); cxform.rotate(curangle, 0.0, 0.0); String chstr = new String(ch, i, 1); g.setTransform(cxform); g.drawString(chstr, (float)(-cwid/2), (float)(-r)); } // compute advance of angle assuming cwid<<radius if (i < (ch.length - 1)) { double adv = cwid/2.0 + fm.getLeading() + getWidth(ch[i + 1],fm)/2.0; // Use of atan() suggested by Michael Moradzadeh curangle += Math.atan(adv / r); // Original code was: // curangle += Math.sin(adv / r); } } } /** * Get the width of a given character under the * specified FontMetrics, interpreting all spaces as * en-spaces. */ static int getWidth(char c, FontMetrics fm) { if (c == ' ' || Character.isSpaceChar(c)) { return fm.charWidth('n'); } else { return fm.charWidth(c); } } public static void main(String args[]) { CircleTextDemo ctd; ctd = new CircleTextDemo(); } class FramePrinter implements ActionListener { private Frame fr; public FramePrinter(Frame f) { fr = f; } public void actionPerformed(ActionEvent ae) { PrintJob pjob; pjob = fr.getToolkit().getPrintJob(fr, "Printing Circle Demo", null, null); if (pjob != null) { Graphics g = pjob.getGraphics(); if (g != null) { g.translate(100,100); fr.printAll(g); g.dispose(); } pjob.end(); } } } }
import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; /** * A small extension to JPanel, meant to allow * the JPanel to support a tiling image background. * The tiled background is correctly drawn inside * any Border that the panel might have. Note * that JTiledPanel containers are always opaque. * If you give the tiling image as null, then * JTiledPanel behaves exactly like an opaque * JPanel. */ public class JTiledPanel extends JPanel { private Image tileimage; private int tilewidth; private int tileheight; private Rectangle rb; private Insets ri; /** * Create a JTiledPanel with the given image. * The tile argument may be null, you can set it later * with setTileImage(). Note that a JTiledPanel is * always opaque. */ public JTiledPanel(Image tile) { super(); setTileImage(tile); setOpaque(true); rb = new Rectangle(0,0,1,1); ri = new Insets(0,0,0,0); } /** * Create a JTiledPanel with the given image and * layout manager and double buffering status. * Either or both of the first two arguments * may be null. */ public JTiledPanel(Image tile, LayoutManager mgr, boolean isDB) { super(mgr, isDB); setTileImage(tile); setOpaque(true); rb = new Rectangle(0,0,1,1); ri = new Insets(0,0,0,0); } /** * Get the current tiling image, or null if there * isn't any right now. */ public Image getTileImage() { return tileimage; } /** * Set the current tiling image. To prevent tiling, * call this method with null. Note that this method * does NOT call repaint for you; if you want the panel * to repaint immediately, you must call repaint() * yourself. */ public void setTileImage(Image tile) { tileimage = tile; tilewidth = 0; tileheight = 0; } /** * Paint this component, including the tiled * background image, if any. */ public void paintComponent(Graphics g) { super.paintComponent(g); if (tileimage != null && tilewidth <= 0) { tileheight = tileimage.getHeight(this); tilewidth = tileimage.getWidth(this); } if (tileimage != null && tilewidth > 0) { Color bg = getBackground(); getBounds(rb); Insets riv = getInsets(ri); rb.translate(riv.left, riv.top); rb.width -= (riv.left + riv.right); rb.height -= (riv.top + riv.bottom); Shape ccache = g.getClip(); g.clipRect(rb.x, rb.y, rb.width, rb.height); int xp, yp; for(yp = rb.y; yp < rb.y + rb.height; yp += tileheight) { for(xp = rb.x; xp < rb.x + rb.width; xp += tilewidth) { g.drawImage(tileimage, xp, yp, bg, this); } } g.setClip(ccache); } } /** * Small main to do a self-test. Tiles with a image file * name taken from the command line. For example, if you * have a directory named images and an image in it * named tile1.gif then you would run this test * main as java JTiledPanel images/tile1.gif. */ public static void main(String [] args) { if (args.length == 0) { System.out.println("Usage: java JTiledPanel imagefile"); System.exit(0); } JFrame f = new JFrame("Test JTiledPanel " + args[0]); ImageIcon ic = new ImageIcon(args[0]); JTiledPanel jtp = new JTiledPanel(ic.getImage()); jtp.setBorder(BorderFactory.createMatteBorder(3,4,5,6,Color.green)); jtp.add(new JButton("Press Me!")); jtp.add(new JButton("Press Me Too!")); f.getContentPane().add(jtp, BorderLayout.CENTER); f.setSize(350,290); f.show(); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { System.exit(0); } }); } }
This code example illustrates using the facilities of the java.awt and java.awt.image packages to combine two images into one. The program accepts two images, displays them, and displays the combination image. This example requires Java 1.1.
--- ImageCombiner.java ---import java.awt.*; import java.awt.image.*; import java.awt.event.*; /** * To use this program, first identify the two images * you want to merge. Then run this program and give * the two image file names on the command line. The * program will display both images and their combination. * To change the relative weights of the combination, * pass both the file names and the weights on the command * line. ** For example: * java ImageCombiner photo1.jpg 0.67 photo2.jpg 0.55 */ public class ImageCombiner extends WindowAdapter { Image resultImage; Image img1; Image img2; Image imgr; ImageDisplay ic1; ImageDisplay ic2; ImageDisplay icr; int ccnt; public class ImageDisplay extends Canvas { private Image i; private Frame myframe; public ImageDisplay(Image img, boolean makeframe) { i = img; if (makeframe) { myframe = new Frame("Image"); myframe.add(this, BorderLayout.CENTER); MediaTracker mt = new MediaTracker(this); mt.addImage(i, 1); try { mt.waitForAll(); } catch (Exception ie) { } setSize(i.getWidth(this), i.getHeight(this)); myframe.pack(); } else { myframe = null; setSize(i.getWidth(this), i.getHeight(this)); } } public void paint(Graphics g) { g.drawImage(i, 0, 0, this); } public Frame getFrame() { return myframe; } public void placeAndShow(int x, int y) { if (myframe != null) { myframe.setLocation(x,y); myframe.show(); } } } /** * ImageMerger acts as an ImageProducer to * generate the merger of two images. * When created, it expects to Image objects. * It extracts the pixels from these objects, * and merges them using a simple weighted * addition in RGB space. The width and height * of the result image are each the maximum of * the widths and heights of the input images. * Normally, you'll get best results when the * images are exactly the same size. */ public class ImageMerger { protected double w1, w2; protected Image i1; protected Image i2; protected ColorModel cm; int rwid, rhgt; protected int results[]; /** * Create in ImageMerger object to merge two * images. This does not perform the merger, * that should be done by calling generate(). * This constructors sets the weights to * 0.5 and 0.5. */ public ImageMerger(Image img1, Image img2) { cm = null; i1 = img1; i2 = img2; w1 = 0.5; w2 = 0.5; rwid = 0; rhgt = 0; results = null; } /** * Set the relative weights of the * two images. Usually, these should add * up to 1.0, but they don't have to. */ public void setWeights(double img1weight, double img2weight) { w1 = img1weight; w2 = img2weight; } /** * Generate the merged image and store it * for later hand-off to an ImageConsumer. * The caller must supply a Component c on which * the image will eventually be drawn. */ public boolean generate(Component comp) { MediaTracker mt; mt = new MediaTracker(comp); mt.addImage(i1, 1); mt.addImage(i2, 2); try { mt.waitForAll(); } catch (Exception ie) { } int wid1, wid2; int hgt1, hgt2; wid1 = i1.getWidth(comp); wid2 = i2.getWidth(comp); hgt1 = i1.getHeight(comp); hgt2 = i2.getHeight(comp); rwid = Math.max(wid1, wid2); rhgt = Math.max(hgt1, hgt2); results = new int[rwid * rhgt]; int [] p1 = new int[rwid * rhgt]; int [] p2 = new int[rwid * rhgt]; PixelGrabber pg1 = new PixelGrabber(i1, 0, 0, wid1, hgt1, p1, 0, rwid); try { pg1.grabPixels(); } catch (Exception ie1) { } PixelGrabber pg2 = new PixelGrabber(i2, 0, 0, wid2, hgt2, p2, 0, rwid); try { pg2.grabPixels(); } catch (Exception ie2) { } cm = ColorModel.getRGBdefault(); int y, x, rp, rpi; int red1, red2, redr; int green1, green2, greenr; int blue1, blue2, bluer; int alpha1, alpha2, alphar; double wgt1, wgt2; for(y = 0; y < rhgt; y++) { for(x = 0; x < rwid; x++) { rpi = y * rwid + x; rp = 0; blue1 = p1[rpi] & 0x00ff; blue2 = p2[rpi] & 0x00ff; green1 = (p1[rpi] >> 8) & 0x00ff; green2 = (p2[rpi] >> 8) & 0x00ff; red1 = (p1[rpi] >> 16) & 0x00ff; red2 = (p2[rpi] >> 16) & 0x00ff; alpha1 = (p1[rpi] >> 24) & 0x00ff; alpha2 = (p2[rpi] >> 24) & 0x00ff; // Computations for combining the pixels, // perform this any way you like! // Here we just use simple weighted addition. wgt1 = w1 * (alpha1 / 255.0); wgt2 = w2 * (alpha2 / 255.0); redr = (int)(red1 * wgt1 + red2 * wgt2); redr = (redr < 0)?(0):((redr>255)?(255):(redr)); greenr = (int)(green1 * wgt1 + green2 * wgt2); greenr = (greenr < 0)?(0):((greenr>255)?(255):(greenr)); bluer = (int)(blue1 * wgt1 + blue2 * wgt2); bluer = (bluer < 0)?(0):((bluer>255)?(255):(bluer)); alphar = 255; rp = (((((alphar << 8) + (redr & 0x0ff)) << 8) + (greenr & 0x0ff)) << 8) + (bluer & 0x0ff); // save the pixel results[rpi] = rp; } } return true; } /** * Simple approach to getting an image - just * create one using MemoryImageSource. */ public Image getGeneratedImage() { Image ret; MemoryImageSource mis; if (results == null) { Frame dummy = new Frame(); generate(dummy); dummy.dispose(); } mis = new MemoryImageSource(rwid, rhgt, cm, results, 0, rwid); ret = Toolkit.getDefaultToolkit().createImage(mis); return ret; } /** * Call this to free up pixel storage allocated by * this ImageMerger object. */ public void dispose() { results = null; return; } } public ImageCombiner(Image i1, double w1, Image i2, double w2) { ccnt = 0; img1 = i1; img2 = i2; ic1 = new ImageDisplay(img1, true); ic1.getFrame().addWindowListener(this); ic1.placeAndShow(50,50); ic2 = new ImageDisplay(img2, true); ic2.getFrame().addWindowListener(this); ic2.placeAndShow(100,90); ImageMerger imerge = new ImageMerger(i1, i2); imerge.setWeights(w1, w2); imerge.generate(ic2); imgr = imerge.getGeneratedImage(); imerge.dispose(); icr = new ImageDisplay(imgr, true); icr.getFrame().addWindowListener(this); icr.placeAndShow(200,180); } public void windowClosing(WindowEvent we) { we.getWindow().hide(); we.getWindow().dispose(); ccnt++; if (ccnt >= 3) System.exit(0); } public static void main(String [] args) { if (args.length < 2) { System.err.println("Too few command-line arguments!"); System.err.println("Usage: java ImageCombiner imagefile1 imagefile2"); System.err.println("Usage: java ImageCombiner imagefile1 weight1 imagefile2 weight2"); System.exit(0); } String imgf1, imgf2; double w1 = 0.5; double w2 = 0.5; if (args.length < 4) { imgf1 = args[0]; imgf2 = args[1]; } else { imgf1 = args[0]; w1 = Double.parseDouble(args[1]); imgf2 = args[2]; w2 = Double.parseDouble(args[3]); } Image i1 = Toolkit.getDefaultToolkit().createImage(imgf1); Image i2 = Toolkit.getDefaultToolkit().createImage(imgf2); if (i1 == null) { System.err.println("Could not get image from " + imgf1); System.exit(1); } if (i2 == null) { System.err.println("Could not get image from " + imgf2); System.exit(2); } ImageCombiner icom = new ImageCombiner(i1, w1, i2, w2); } }
This code example illustrates using the facilities of the javax.imageio and java.awt.image packages to read, process, and write out image files. This program also prints a little bit of diagnostic information about JAI ImageIO. This example requires Java 2 and J2SE 1.4 beta 2 or later, or J2SE 1.3 and the JAI-IIO package.
--- IIOTest1.java ---import java.io.*; import java.util.*; import java.awt.image.*; import javax.imageio.*; import javax.imageio.event.*; import javax.imageio.stream.*; public class IIOTest1 { PrintStream out; String iname, oname; BufferedImage im; public IIOTest1(String i) { this(System.out, i, null); } public IIOTest1(PrintStream ps, String i, String o) { out = ps; iname = i; oname = o; im = null; } public void msg(String s) { out.println(s); } public void msg(String p, String [] l) { for(int i = 0; i < l.length; i++) { out.print(p + (i+1) + ". "); out.println(l[i]); } } public void printInfo() { String fmts[]; msg("IIO API Info:"); msg(" Available read formats:"); fmts = ImageIO.getReaderFormatNames(); msg("\t", fmts); msg(" Available read MIME Types:"); fmts = ImageIO.getReaderMIMETypes(); msg("\t", fmts); msg(" Available write formats:"); fmts = ImageIO.getWriterFormatNames(); msg("\t", fmts); } public boolean doRead() { ImageReader r = null; boolean ret = false; try { String ext = getExtension(iname); msg("Got extension: '" + ext + "'"); Iterator readers = ImageIO.getImageReadersBySuffix(ext); if (!(readers.hasNext())) { msg("No readers for extension " + ext); return false; } r = (ImageReader)(readers.next()); if (r == null) { msg("No readers for extension " + ext); return false; } File fi = new File(iname); ImageInputStream iis = ImageIO.createImageInputStream(fi); r.setInput(iis, true); msg("Created reader and stream."); r.addIIOReadProgressListener(new ReadProgressReporter(out)); im = r.read(0); if (im == null) { msg("Unable to read image 0 ?!"); ret = false; } else { msg("Read image " + iname + " okay, size=" + im.getWidth() + "x" + im.getHeight()); ret = true; } } catch (IIOException iioe) { msg("ImageIO read Exception: " + iioe); iioe.printStackTrace(out); ret = false; } catch (Exception e) { msg("Other exception: " + e); e.printStackTrace(out); ret = false; } finally { if (r != null) r.dispose(); } return ret; } public boolean doWrite() { boolean ret = false; ImageWriter w = null; if (oname == null) return false; try { String ext = getExtension(oname); msg("Got output extension: '" + ext + "'"); Iterator writers = ImageIO.getImageWritersBySuffix(ext); if (!(writers.hasNext())) { msg("No readers for extension " + ext); return false; } w = (ImageWriter)(writers.next()); if (w == null) { msg("No writers for extension " + ext); return false; } File fo = new File(oname); ImageOutputStream ios = ImageIO.createImageOutputStream(fo); w.setOutput(ios); WriteProgressReporter wpr = new WriteProgressReporter(out); w.addIIOWriteProgressListener(wpr); msg("Created stream and writer."); w.write(im); msg("Apparently able to write image to " + oname); ret = true; } catch (IIOException iioe) { msg("ImageIO write Exception: " + iioe); iioe.printStackTrace(out); ret = false; } catch (Exception e) { msg("Other exception: " + e); e.printStackTrace(out); ret = false; } finally { if (w != null) w.dispose(); } return ret; } public static float blur[] = { 0.001F, 0.005F, 0.005F, 0.005F, 0.001F, 0.005F, 0.1F, 0.1F, 0.1F, 0.005F, 0.005F, 0.1F, 0.3F, 0.1F, 0.005F, 0.005F, 0.1F, 0.1F, 0.1F, 0.005F, 0.001F, 0.005F, 0.005F, 0.005F, 0.001F }; public void doProcessing() { if (im == null) return; int wid = im.getWidth(); int hgt = im.getHeight(); Raster imr = im.getData(); msg("Beginning processing"); WritableRaster newr = imr.createCompatibleWritableRaster(); Kernel blurkernel = new Kernel(5,5,blur); ConvolveOp op = new ConvolveOp(blurkernel, ConvolveOp.EDGE_ZERO_FILL, null); op.filter(imr, newr); msg("Creating new image"); BufferedImage im2 = new BufferedImage(im.getColorModel(), newr, im.isAlphaPremultiplied(), null); im = im2; } public class ReadProgressReporter implements IIOReadProgressListener { PrintStream ps; public ReadProgressReporter(PrintStream p) { ps = p; } // event handling methods for reading public void sequenceStarted(ImageReader source, int minIndex) { ps.println(" listener: Read sequence started."); } public void sequenceComplete(ImageReader source) { ps.println(" listener: Read sequence completed."); } public void imageStarted(ImageReader source, int imageIndex) { ps.println(" listener: image started, index=" + imageIndex); } public void imageProgress(ImageReader source, float percentageDone) { ps.print("."); } public void imageComplete(ImageReader source) { ps.println("\n listener: Image read completed."); } public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) { } public void thumbnailProgress(ImageReader source, float percentageDone) { } public void thumbnailComplete(ImageReader source) { } public void readAborted(ImageReader source) { ps.println(" listener: Image read aborted."); } } public class WriteProgressReporter implements IIOWriteProgressListener { PrintStream ps; public WriteProgressReporter(PrintStream p) { ps = p; } // event handling methods for writing public void imageStarted(ImageWriter source, int imageIndex) { ps.println(" listener: Image write started."); } public void imageProgress(ImageWriter source, float percentageDone) { ps.print(">"); } public void imageComplete(ImageWriter source) { ps.println("\n listener: Image write completed."); } public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) { } public void thumbnailProgress(ImageWriter source, float percentageDone) { } public void thumbnailComplete(ImageWriter source) { } public void writeAborted(ImageWriter source) { ps.println("\n listener: Image write aborted."); } } public final static String getExtension(String name) { int eix = name.lastIndexOf('.'); if (eix < 0) return null; else return name.substring(eix + 1); } public static void main(String [] args) { String iname = null; String oname = null; if (args.length < 1) { System.out.println("Usage: java IIOTest1 infile [outfile]"); System.exit(0); } iname = args[0]; if (args.length >1) oname = args[1]; IIOTest1 t = new IIOTest1(System.out, iname, oname); t.printInfo(); if (t.doRead()) { System.out.println("Read succeeded"); t.doProcessing(); if (t.doWrite()) { System.out.println("Write succeeded."); } else { System.out.println("Write failed."); } } else { System.out.println("Read failed."); } } }
This modest servlet illustrates how to use the AWT and JAI IIO facilities to modify an image in a servlet, and then send the image to the browser. It applies a parameter-specified text label in the upper-left corner of the image. This example requires the servlet framework and J2SE 1.4.
--- ImageLabelServlet.java ---import java.awt.*; import java.awt.image.*; import javax.imageio.*; import javax.imageio.stream.*; import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /* * This servlet provides the service of labeling an * image with a text string. The caller of the * servlet gets to specify the image (local file or * URL), the text string, text size, and the color. * This servlet has a large number of limitations, * the foremost being that it will only write JPEG * images (with minimal compression). ** Note: on X-based systems like Linux or Solaris, * this servlet probably won't work unless the host * is running an X server. *
* This servlet requires the JAI/IIO package to be * installed to perform JPG image output. *
* Example img src URL:
* http://www.myhost.com/servlet/ImageLabelServer?src=http://www.foobar.com/logo.jpg&text=Foobar+Logo&size=28&color=90f000ff *
* This sample will cause the servlet to got retrieve the image logo.jpg from * the www.foobar.com web site, and then overlay the text "Foobar Logo" in * partially transparent purple 28-point text. * * @author Neal Ziring */ public class ImageLabelServlet extends HttpServlet { public static final String PARM_IMAGE = "src"; public static final String PARM_TEXT = "text"; public static final String PARM_SIZE = "size"; public static final String PARM_COLOR= "color"; public static final String IMG_OUT_SUFFIX = "jpg"; public static final String IMG_OUT_MIME = "image/jpeg"; /* need these for doing AWT stuff */ static Component ob = null; static Toolkit tk = null; /** * Initialize stuff we need for this servlet. */ public void init(ServletConfig conf) { if (tk == null) tk = Toolkit.getDefaultToolkit(); if (ob == null) { ob = new Frame(); ob.addNotify(); } } /** * Just call the doGet method to handles posts. */ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { doGet(req,resp); } /** * Process a request - we look for the parameters * listed above. Our output consists of an image * in image/jpeg format, or a 404 message if we * couldn't load the requested image. */ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { String src = req.getParameter(PARM_IMAGE); if (src == null || src.length() == 0) { resp.sendError(resp.SC_BAD_REQUEST, "No source image specified"); return; } String text = req.getParameter(PARM_TEXT); if (text == null || text.length() == 0) text = (new Date()).toString(); String size = req.getParameter(PARM_SIZE); int textSize = 14; try { textSize = Integer.parseInt(size); } catch (Exception ie) { } String color = req.getParameter(PARM_COLOR); int textColor = 0x666666; try { if (color.startsWith("#")) color = color.substring(1); textColor = (int)(Long.parseLong(color, 16) & 0x0ffffffff); } catch (Exception ie2) { } float cq = 0.01F; String qual = req.getParameter("q"); if (qual != null && qual.length() > 0) { try { cq = (float)(Double.parseDouble(qual)); } catch (Exception ie) { } } Font f = new Font("Sans-serif", Font.BOLD, textSize); Image img = null; if (src.indexOf('/') >= 0 || src.indexOf(':') >= 0) { try { URL u = new URL(src); img = tk.createImage(u); } catch (Exception ie3) { } } else { try { img = tk.createImage(src); } catch (Exception ie4) { } } MediaTracker mt = new MediaTracker(ob); mt.addImage(img, 2); try { mt.waitForID(2); } catch (InterruptedException eix) { } if (mt.isErrorID(2)) img = null; if (img == null) { resp.sendError(resp.SC_NOT_FOUND, "Could not load source image"); } int w, h; w = img.getWidth(ob); h = img.getHeight(ob); BufferedImage img2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); if (img2 == null) System.err.println("img2 is Null!"); Graphics g = img2.getGraphics(); if (g == null) System.err.println("Graphics is Null!"); Color c = new Color(textColor, (((textColor >> 24) & 0xff) != 0)); g.setColor(c); g.setFont(f); g.drawImage(img, 0, 0, ob); g.drawString(text, textSize/2, (textSize * 5)/4); img.flush(); img = null; Iterator writers = ImageIO.getImageWritersBySuffix(IMG_OUT_SUFFIX); if (!(writers.hasNext())) { resp.sendError(resp.SC_CONFLICT, "Cannot write JPEG image!"); return; } if (!writeImageOut((ImageWriter)(writers.next()), img2, resp, cq)) { resp.sendError(resp.SC_CONFLICT, "Writing image failed"); return; } img2.flush(); img2 = null; return; } /** * Write out the image to the servlet response stream. * This method always sends a mime type of IMG_OUT_MIME. */ boolean writeImageOut(ImageWriter wr, BufferedImage img, HttpServletResponse resp, float cq) { boolean ret = false; resp.setContentType(IMG_OUT_MIME); try { OutputStream oos = resp.getOutputStream(); ImageOutputStream ios = ImageIO.createImageOutputStream(oos); wr.setOutput(ios); ImageWriteParam parm = wr.getDefaultWriteParam(); parm.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); parm.setCompressionQuality(cq); cq = parm.getCompressionQuality(); IIOImage ii = new IIOImage(img, null, null); wr.prepareWriteSequence(null); wr.writeToSequence(ii, parm); wr.endWriteSequence(); ios.flush(); wr.dispose(); ios.close(); ret = true; } catch ( Exception ioe) { System.err.println("Error in image writing: " + ioe); ioe.printStackTrace(); } return ret; } }
This Swing utility class incorporates concepts from several parts of the JFC to provide a useful service: a ProgressMonitor for the transfer of data from one stream to another (e.g. from a file to a Socket). It illustrates correct Swing thread handling, use of the Swing Timer class, and use of the Swing ProgressMonitor. This sample requires the Java 2 and JDK 1.3 or later.
--- ProgressMonitoredDataTransfer.java ---import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; /** * Transfer data from one stream to another, * providing a Swing ProgressMonitor to handle * notifying the user and allowing the user to * cancel the transfer. The progress updates * every half-second. *Objects of this class * also act as Observables, updating their * observers after each block of data is * transfered, and again at the end of the * transfer. */ public class ProgressMonitoredDataTransfer extends java.util.Observable implements Runnable, ActionListener { private InputStream readFrom; private OutputStream writeTo; private ProgressMonitor monitor; private boolean done; private int max; private int current; private Timer mytimer; private Thread mythread; private boolean closeWhenDone; public static final int DELAY = 500; public static final int BUFSIZ = 1024; /** * Create a progress monitored data transfer. * * @param par Parent Swing Window or Frame * @param from Stream to read data from * @param to Stream to write data out to * @param maxtransfer Maximum number of bytes we expect to transfer * @param message Progress monitor message object (usually String) * @param close If set, then close the streams at end of transfer */ public ProgressMonitoredDataTransfer(Component par, InputStream from, OutputStream to, int maxtransfer, Object message, boolean close) { readFrom = new BufferedInputStream(from); writeTo = to; max = maxtransfer; monitor = new ProgressMonitor(par, message, null, 0, max); monitor.setMillisToPopup(2*DELAY); mythread = new Thread(this); mytimer = new Timer(DELAY, this); done = false; closeWhenDone = close; current = 0; mythread.start(); mytimer.start(); } /** * Handle timer actions. */ public void actionPerformed(ActionEvent e) { if (isDone()) { // if the transfer is done, close the monitor monitor.close(); mytimer.stop(); } else if (monitor.isCanceled()) { // if the user hits cancel, then interrupt our thread if (mythread != null && mythread.isAlive()) mythread.interrupt(); monitor.close(); mytimer.stop(); } else { // otherwise, just // retrieve the current progress value and... int cur = getCurrent(); // ...then update the monitor! monitor.setProgress(cur); } } /** * Override this to handle problems differently. Note that * this method may or may not be called from the * Swing event thread. The default implementation * just prints to System.err. * * @param msg Message about the problem */ public void handleProblem(String msg) { System.err.println("Transfer problem: " + msg); } /** * Retrieve the value of current, which is the * number of bytes transferred so far. * * @returns number of bytes transfered */ public synchronized int getCurrent() { return current; } private synchronized void addToCurrent(int x) { current += x; } /** * Check whether the task is done yet. */ public synchronized boolean isDone() { return done; } private synchronized void setDone() { done = true; setChanged(); notifyObservers("Done"); } /** * Wait for the task to be done. * It is probably not a good idea to use this in Swing. */ public void waitForDone() { try { mythread.join(); } catch (InterruptedException ie) { } return; } /** * Dont override this. */ public void run() { try { byte buf[] = new byte[BUFSIZ]; int cc; for(cc = readFrom.read(buf, 0, BUFSIZ); cc > 0; cc = readFrom.read(buf, 0, BUFSIZ)) { writeTo.write(buf, 0, cc); addToCurrent(cc); // update the current progress value // Thread.sleep(200); if (mythread.isInterrupted()) break; setChanged(); notifyObservers(null); } } catch (InterruptedIOException ioe) { handleProblem("Cancelled by user after " + current + " bytes."); } catch (Exception ie) { handleProblem("IO exception: " + ie); } finally { setDone(); if (closeWhenDone) { if (readFrom != null) { try { readFrom.close(); } catch (Exception e2) { } } if (writeTo != null) { try { writeTo.close(); } catch (Exception e3) { } } } } } }